mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
Refactor of etl::buffer_descriptors interface
This commit is contained in:
parent
56192a06aa
commit
d33d32be23
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user