Refactor of etl::buffer_descriptors interface

This commit is contained in:
John Wellbelove 2020-11-23 12:33:04 +00:00
parent 56192a06aa
commit d33d32be23
6 changed files with 194 additions and 93 deletions

View File

@ -37,33 +37,34 @@ SOFTWARE.
#include "type_traits.h"
#include "static_assert.h"
#include "cyclic_value.h"
#include "algorithm.h"
#include <cstring>
#undef ETL_FILE
#define ETL_FILE "57"
namespace etl
{
//***************************************************************************
/// buffer_descriptors
//***************************************************************************
template <typename TBuffer, typename TSize, TSize BUFFER_SIZE_, size_t N_BUFFERS_, typename TFlag = bool>
template <typename TBuffer, size_t BUFFER_SIZE_, size_t N_BUFFERS_, typename TFlag = bool>
class buffer_descriptors
{
private:
struct descriptor_item;
public:
typedef TBuffer value_type;
typedef value_type* pointer;
typedef TSize size_type;
typedef TFlag flag_type;
typedef TBuffer value_type;
typedef value_type* pointer;
typedef size_t size_type;
typedef TFlag flag_type;
ETL_STATIC_ASSERT(etl::is_unsigned<TSize>::value, "TSize must be unsigned");
ETL_STATIC_ASSERT(etl::is_integral<TSize>::value, "TSize must be integral");
static ETL_CONSTANT size_t N_BUFFERS = N_BUFFERS_;
static ETL_CONSTANT size_type N_BUFFERS = N_BUFFERS_;
static ETL_CONSTANT size_type BUFFER_SIZE = BUFFER_SIZE_;
//*********************************
/// Describes a buffer.
//*********************************
class descriptor
{
@ -75,78 +76,131 @@ namespace etl
//*********************************
descriptor()
: pbuffer(ETL_NULLPTR)
, in_use(false)
: pdesc_item(ETL_NULLPTR)
{
}
//*********************************
ETL_CONSTEXPR pointer data() const
descriptor(const descriptor& other)
: pdesc_item(other.pdesc_item)
{
return pbuffer;
}
//*********************************
ETL_CONSTEXPR size_type max_size() const
descriptor& operator =(const descriptor& other)
{
pdesc_item = other.pdesc_item;
return *this;
}
//*********************************
pointer data() const
{
return pdesc_item->pbuffer;
}
//*********************************
ETL_NODISCARD
constexpr size_type max_size() const
{
return BUFFER_SIZE;
}
//*********************************
bool is_valid() const
{
return pbuffer != ETL_NULLPTR;
}
//*********************************
ETL_NODISCARD
bool is_allocated() const
{
return bool(in_use);
return bool(pdesc_item->in_use);
}
//*********************************
ETL_NODISCARD
bool is_released() const
{
return !bool(in_use);
return bool(!pdesc_item->in_use);
}
//*********************************
ETL_NODISCARD
bool is_valid() const
{
return pdesc_item != ETL_NULLPTR;
}
//*********************************
void release()
{
in_use = false;
pdesc_item->in_use = false;
}
private:
//*********************************
ETL_CONSTEXPR descriptor(TBuffer* pbuffer_)
: pbuffer(pbuffer_)
, in_use(false)
descriptor(descriptor_item* pdesc_item_)
: pdesc_item(pdesc_item_)
{
}
//*********************************
void allocate()
{
in_use = true;
pdesc_item->in_use = true;;
}
/// The pointer to the buffer descriptor.
descriptor_item* pdesc_item;
};
//*********************************
/// Describes a notification.
//*********************************
class notification
{
public:
//*********************************
notification()
: desc()
, count(0U)
{
}
//*********************************
descriptor(const descriptor&) ETL_DELETE;
descriptor& operator =(const descriptor&) ETL_DELETE;
notification(descriptor desc_, size_t count_)
: desc(desc_)
, count(count_)
{
}
pointer pbuffer;
volatile flag_type in_use;
//*********************************
ETL_NODISCARD
descriptor get_descriptor() const
{
return desc;
}
//*********************************
ETL_NODISCARD
size_t get_count() const
{
return count;
}
private:
descriptor desc;
size_t count;
};
typedef etl::delegate<void(descriptor&, size_type)> callback_type;
// The type of the callback function.
typedef etl::delegate<void(notification)> callback_type;
//*********************************
buffer_descriptors(TBuffer* pbuffers_)
{
for (size_t i = 0U; i < N_BUFFERS; ++i)
{
descriptors[i].pbuffer = pbuffers_ + (i * BUFFER_SIZE);
descriptor_items[i].pbuffer = pbuffers_ + (i * BUFFER_SIZE);
}
}
@ -156,7 +210,7 @@ namespace etl
{
for (size_t i = 0U; i < N_BUFFERS; ++i)
{
descriptors[i].pbuffer = pbuffers_ + (i * BUFFER_SIZE);
descriptor_items[i].pbuffer = pbuffers_ + (i * BUFFER_SIZE);
}
}
@ -167,26 +221,27 @@ namespace etl
}
//*********************************
ETL_NODISCARD
bool is_valid() const
{
return callback.is_valid();
}
//*********************************
void notify(descriptor& desc_, size_type count_)
void notify(notification n)
{
// We have a valid callback?
// Do we have a valid callback?
if (callback.is_valid())
{
// Set the relevant data.
callback(desc_, count_);
}
callback(n);
}
}
//*********************************
descriptor& allocate()
ETL_NODISCARD
descriptor allocate()
{
descriptor& desc = descriptors[next];
descriptor desc(&descriptor_items[next]);
if (desc.is_released())
{
@ -198,19 +253,37 @@ namespace etl
}
else
{
static descriptor null_descriptor;
return null_descriptor;
return descriptor();
}
}
//*********************************
ETL_NODISCARD
descriptor allocate(value_type fill_)
{
descriptor desc = allocate();
if (desc.is_valid())
{
etl::fill_n(desc.data(), BUFFER_SIZE, fill_);
}
return desc;
}
private:
//*********************************
struct descriptor_item
{
pointer pbuffer;
volatile flag_type in_use;
};
callback_type callback;
etl::array<descriptor, N_BUFFERS> descriptors;
etl::array<descriptor_item, N_BUFFERS> descriptor_items;
etl::cyclic_value<uint_least8_t, 0U, N_BUFFERS - 1> next;
};
}
#undef ETL_FILE
#endif

View File

@ -38,7 +38,7 @@ SOFTWARE.
///\ingroup utilities
#define ETL_VERSION_MAJOR 19
#define ETL_VERSION_MINOR 0
#define ETL_VERSION_MINOR 1
#define ETL_VERSION_PATCH 0
#define ETL_VERSION ETL_STRINGIFY(ETL_VERSION_MAJOR) "." ETL_STRINGIFY(ETL_VERSION_MINOR) "." ETL_STRINGIFY(ETL_VERSION_PATCH)
#define ETL_VERSION_W ETL_STRINGIFY(ETL_VERSION_MAJOR) L"." ETL_STRINGIFY(ETL_VERSION_MINOR) L"." ETL_STRINGIFY(ETL_VERSION_PATCH)

View File

@ -1,6 +1,6 @@
{
"name": "Embedded Template Library",
"version": "19.0.0",
"version": "19.1.0",
"author s": {
"name": "John Wellbelove",
"email": "john.wellbelove@etlcpp.com"

View File

@ -1,5 +1,5 @@
name=Embedded Template Library
version=19.0.0
version=19.1.0
author= John Wellbelove <john.wellbelove@etlcpp.com>
maintainer=John Wellbelove <john.wellbelove@etlcpp.com>
license=MIT

View File

@ -1,3 +1,7 @@
===============================================================================
19.1.0
Refactor of etl::buffer_descriptors interface.
===============================================================================
19.0.0
Containers with external buffers or pools are now separate classes instead of specialisations.

View File

@ -52,17 +52,15 @@ namespace
constexpr size_t N_BUFFERS = 4U;
constexpr size_t DATA_COUNT = BUFFER_SIZE / 2;
using BD = etl::buffer_descriptors<char, uint_least8_t, BUFFER_SIZE, N_BUFFERS, std::atomic_char>;
using Descriptor = BD::descriptor;
using NotificationCallback = BD::callback_type;
using BD = etl::buffer_descriptors<char, BUFFER_SIZE, N_BUFFERS, std::atomic_char>;
//***********************************
struct Receiver
{
void receive(Descriptor& desc_, BD::size_type count_)
void receive(BD::notification n)
{
pbuffer = desc_.data();
count = count_;
pbuffer = n.get_descriptor().data();
count = n.get_count();
}
BD::pointer pbuffer;
@ -89,7 +87,7 @@ namespace
Receiver receiver;
char buffers[N_BUFFERS][BUFFER_SIZE];
NotificationCallback callback = NotificationCallback::create<Receiver, &Receiver::receive>(receiver);
BD::callback_type callback = BD::callback_type::create<Receiver, &Receiver::receive>(receiver);
BD bd(&buffers[0][0], callback);
@ -104,7 +102,7 @@ namespace
Receiver receiver;
char buffers[N_BUFFERS][BUFFER_SIZE];
NotificationCallback callback = NotificationCallback::create<Receiver, &Receiver::receive>(receiver);
BD::callback_type callback = BD::callback_type::create<Receiver, &Receiver::receive>(receiver);
BD bd(&buffers[0][0]);
bd.set_callback(callback);
@ -120,19 +118,45 @@ namespace
Receiver receiver;
char buffers[N_BUFFERS][BUFFER_SIZE];
NotificationCallback callback = NotificationCallback::create<Receiver, &Receiver::receive>(receiver);
BD::callback_type callback = BD::callback_type::create<Receiver, &Receiver::receive>(receiver);
BD bd(&buffers[0][0], callback);
for (size_t i = 0U; i < N_BUFFERS; ++i)
{
Descriptor& desc = bd.allocate();
BD::descriptor desc = bd.allocate();
CHECK_EQUAL(BUFFER_SIZE, desc.max_size());
CHECK_EQUAL(uintptr_t(&buffers[i][0]), uintptr_t(desc.data()));
}
}
//*************************************************************************
TEST(test_buffers_with_allocate_fill)
{
Receiver receiver;
std::array<char, BUFFER_SIZE> test =
{
char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF),
char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF), char(0xFF)
};
char buffers[N_BUFFERS][BUFFER_SIZE];
BD::callback_type callback = BD::callback_type::create<Receiver, &Receiver::receive>(receiver);
BD bd(&buffers[0][0], callback);
for (size_t i = 0U; i < N_BUFFERS; ++i)
{
BD::descriptor desc = bd.allocate(char(0xFF));
CHECK_EQUAL(BUFFER_SIZE, desc.max_size());
CHECK_EQUAL(uintptr_t(&buffers[i][0]), uintptr_t(desc.data()));
CHECK_ARRAY_EQUAL(test.data(), desc.data(), BUFFER_SIZE);
}
}
//*************************************************************************
TEST(test_notifications)
{
@ -143,18 +167,18 @@ namespace
char buffers[N_BUFFERS][BUFFER_SIZE];
std::fill(&buffers[0][0], &buffers[N_BUFFERS - 1][0] + BUFFER_SIZE , 0U);
NotificationCallback callback = NotificationCallback::create<Receiver, &Receiver::receive>(receiver);
BD::callback_type callback = BD::callback_type::create<Receiver, &Receiver::receive>(receiver);
BD bd(&buffers[0][0], callback);
for (size_t i = 0U; i < N_BUFFERS; ++i)
{
Descriptor& desc = bd.allocate();
BD::descriptor desc = bd.allocate();
CHECK(desc.is_valid());
std::copy(test.begin(), test.begin() + DATA_COUNT, desc.data());
bd.notify(desc, DATA_COUNT);
bd.notify(BD::notification(desc, DATA_COUNT));
desc.release();
CHECK_EQUAL(DATA_COUNT, receiver.count);
@ -170,18 +194,18 @@ namespace
char buffers[N_BUFFERS][BUFFER_SIZE];
NotificationCallback callback = NotificationCallback::create<Receiver, &Receiver::receive>(receiver);
BD::callback_type callback = BD::callback_type::create<Receiver, &Receiver::receive>(receiver);
BD bd(&buffers[0][0], callback);
// Use up all of the descriptors.
for (size_t i = 0U; i < N_BUFFERS; ++i)
{
Descriptor& desc = bd.allocate();
BD::descriptor desc = bd.allocate();
CHECK(desc.is_valid());
}
Descriptor& desc = bd.allocate();
BD::descriptor desc = bd.allocate();
CHECK(!desc.is_valid());
}
@ -189,25 +213,25 @@ namespace
TEST(test_allocate_release_rollover)
{
Receiver receiver;
std::queue<Descriptor*> desc_queue;
std::queue<BD::descriptor> desc_queue;
char buffers[N_BUFFERS][BUFFER_SIZE];
NotificationCallback callback = NotificationCallback::create<Receiver, &Receiver::receive>(receiver);
BD::callback_type callback = BD::callback_type::create<Receiver, &Receiver::receive>(receiver);
BD bd(&buffers[0][0], callback);
// Use up all of the descriptors, then release/allocate for the rest.
for (size_t i = 0U; i < (N_BUFFERS * 2); ++i)
{
Descriptor& desc = bd.allocate();
desc_queue.push(&desc);
BD::descriptor desc = bd.allocate();
desc_queue.push(desc);
CHECK(desc.is_valid());
if (i >= (N_BUFFERS - 1))
{
desc_queue.front()->release();
desc_queue.front().release();
desc_queue.pop();
}
}
@ -220,10 +244,10 @@ namespace
BD bd(&buffers[0][0]);
Descriptor& desc1 = bd.allocate();
Descriptor& desc2 = bd.allocate();
Descriptor& desc3 = bd.allocate();
Descriptor& desc4 = bd.allocate();
BD::descriptor desc1 = bd.allocate();
BD::descriptor desc2 = bd.allocate();
BD::descriptor desc3 = bd.allocate();
BD::descriptor desc4 = bd.allocate();
CHECK(desc1.is_allocated());
CHECK(desc2.is_allocated());
@ -279,18 +303,18 @@ namespace
//*********************************
struct Notification
{
Descriptor* pdesc;
BD::descriptor desc;
BD::size_type count;
};
constexpr int N_ITERATIONS = 1000000;
etl::queue_spsc_atomic<Notification, N_ITERATIONS + 100> desc_queue;
etl::queue_spsc_atomic<BD::notification, N_ITERATIONS + 100> desc_queue;
//*********************************
void Callback(Descriptor& desc_, BD::size_type count_)
void Callback(BD::notification n)
{
desc_queue.push(Notification{ &desc_, count_ });
desc_queue.push(n);
}
//*********************************
@ -298,7 +322,7 @@ namespace
{
static char buffers[N_BUFFERS][BUFFER_SIZE];
BD bd(&buffers[0][0], NotificationCallback::create<Callback>());
BD bd(&buffers[0][0], BD::callback_type::create<Callback>());
RAISE_THREAD_PRIORITY;
FIX_PROCESSOR_AFFINITY1;
@ -310,21 +334,21 @@ namespace
for (int i = 0; i < N_ITERATIONS; ++i)
{
Descriptor* pdesc;
BD::descriptor desc;
// Wait until we can allocate a descriptor.
do
{
pdesc = &bd.allocate();
} while (pdesc->is_valid() == false);
desc = bd.allocate();
} while (desc.is_valid() == false);
if (!pdesc->is_allocated())
if (!desc.is_allocated())
{
++errors;
}
// Send a notification to the callback function.
bd.notify(*pdesc, BUFFER_SIZE);
bd.notify(BD::notification(desc, BUFFER_SIZE));
}
CHECK_EQUAL(0, errors);
@ -343,20 +367,20 @@ namespace
for (int i = 0; i < N_ITERATIONS;)
{
Notification n;
BD::notification notification;
// Try to get a notification from the queue.
if (desc_queue.pop(n))
if (desc_queue.pop(notification))
{
CHECK_EQUAL(BUFFER_SIZE, n.count);
CHECK(n.pdesc->is_allocated());
CHECK_EQUAL(BUFFER_SIZE, notification.get_count());
CHECK(notification.get_descriptor().is_allocated());
if (!n.pdesc->is_allocated())
if (!notification.get_descriptor().is_allocated())
{
++errors;
}
n.pdesc->release();
notification.get_descriptor().release();
++i;
}