Цитата
brag, можете показать реализацию variable element-size queue?
Ваша реализация не понравилась, слишком много левого зависимого кода. И куча приведений типов, тем более reinterpret_cast.
Вот моя, без зависимостей вообще. Это самый простой вариант. Текущую версию приводить не буду, чтобы не путались, там локфри, записаь происходит за несколько этапов и сложно для понимания. Разберемся с обычной - перейдем к лок-фри.
Шаблон уже видели, вот его реализация
CODE
public:
typedef uint32_t BufferT;
// Fifo item base class. Must be POD
class ItemBase{
public:
// pointer to function that knows how to call specific lambda
typedef void(*WrapperT)(ItemBase*, TArgs...);
void invoke(TArgs ... args){
wrapper_(this, args...);
}
// ------------------------------------------
static size_t mark_unused(size_t size){
return size | UnusedMask;
}
size_t size()const{ return size_ & ~UnusedMask; }
bool isUsed()const{ return (size_ & UnusedMask) == 0; }
enum{ UnusedMask = 1<<(sizeof(size_t)*8-1) };
// ------------------------------------------
protected:
size_t size_; // in sizeof(BufferT) units
WrapperT wrapper_;
};
private:
// Constructors are private.
// You cant create objects, create VdelegateFIFO_T instead
// But You can use pointers of this type
VdelegateFIFO();
VdelegateFIFO(uint16_t size): size(size){
// empty: rx == wx && pushed_objects == 0
// full: rx == wx && pushed_objects > 0
rx = wx = 0;
pushed_objects = 0;
}
// lambda, function or functor object
template<class T>
class Item : public ItemBase{
public:
Item(const T& la): lambda(la){
this->size_ = obect_size();
this->wrapper_ = [](ItemBase* that, TArgs ... args){
static_cast<Item*>(that)->lambda(args...);
};
}
// in sizeof(ItemBase) units
static constexpr size_t obect_size(){
return sizeof(Item)/sizeof(BufferT);
}
void* operator new(size_t, void *ptr){ return ptr; }
private:
T lambda;
};
// -------------------- public --------------------
public:
template<class T>
bool push(const T& obj){
int w = getFreeConsecutiveSpace(Item<T>::obect_size());
if(w<0){ // not enough space
return false;
}
new(&buffer[w]) Item<T>(obj);
wx = w + Item<T>::obect_size();
if(wx >= size)wx = 0; // wrap
pushed_objects++;
return true;
}
ItemBase* getItemToPop(){
if(isEmpty())return nullptr;
ItemBase *item = reinterpret_cast<ItemBase*>(&buffer[rx]);
// wrap if unused tail
if(!item->isUsed()){
rx = 0;
item = reinterpret_cast<ItemBase*>(&buffer[rx]);
}
return item;
}
bool pop(const ItemBase *item){
if(!item)return false;
if(isEmpty())return false;
pushed_objects--;
rx += item->size();
if(rx>=size){
rx=0; // wrap
}else if(rx>wx){
// pop unused tail of buffer too
ItemBase *item = reinterpret_cast<ItemBase*>(&buffer[rx]);
if(!item->isUsed()){
//rx += item->size();
//if(rx >= size)rx=0;
rx=0; // wrap
}
}
return true;
}
bool isEmpty()const{ return pushed_objects==0; }
// ---------------------------------------------------
private:
int getFreeConsecutiveSpace(int sz){
int w = wx;
if(w==rx && pushed_objects>0)return -1; // full
// wx after rx
if(w >= rx){
if(w+sz <= size)return w;
// mark tail of buffer unused
buffer[w] = ItemBase::mark_unused(size-w);
// wrap
w = 0;
}
// wx before rx
if(w+sz <= rx)return w;
return -1;
}
template<class T, int Size> friend class VdelegateFIFO_T;
private:
const uint16_t size; // in sizeof(BufferT) units
uint16_t rx, wx;
uint16_t pushed_objects;
// it's a bit tricky and non-standard but works
BufferT buffer[0];
};
// ------------------ Container ------------------
template<class T, int Size_w>
class VdelegateFIFO_T : public VdelegateFIFO<T>{
public:
VdelegateFIFO_T(): VdelegateFIFO<T>(Size_w) {}
private:
typedef typename VdelegateFIFO<T>::BufferT BufferT;
BufferT buffer[Size_w];
};
Не знаю, понятен ли этот код, поэтому обясню на пальцах, а там можно и самому сделать.
Выделяется буфер фиксированного размера - это и есть сторейж нашей очереди. Данные пишуться по кругу, ring-buffer. Но с одной оговоркой - если обьект не влазит в конец буфера, этот конец помечается, как unused, а обьект пытаемся поместить в начало буфера. Если и там места нет - значит в очереди нет места для обьекта. Все. Если очередь используется с разных уровней приоритета - нужен лок на всю операцию записи/чтения.
В лок-фри в принципе идея та же, только запись служебных полей и самих данных разделено во времени(на 3 этапа). Еще есть одна реализация, где лок есть только на служебные поля, но запись данных идет без лока, тоже 3-этапная запись. Используется на процах, где нет нужной поддержки(у кортекс это ldrex/strex)
Вот пример использования очереди, ne lock-free , обычной:
CODE
template<int Size_w> //, class TBase=void*>
class TaskQueue{
public:
TaskQueue(SST::TPriority priority):
priority(priority)
{
current_task = nullptr; // dont pop first time
}
template<class T>
bool enqueueTask(const T& la){
CriticalSection cs;
cs.enter();
bool e = fifo.isEmpty();
bool r = fifo.push(la);
cs.leave();
if(e)taskCompleted();
return r;
}
bool taskCompleted(){
bool r = SST::postMessage(priority, [this](){
processNextTask();
});
if(!r){
printf("TaskQueue::taskCompleted error: SST_queue %d full\n", priority);
}
return r;
}
private:
void processNextTask(){
CriticalSection cs;
cs.enter();
fifo.pop(current_task); // pop if exists
auto la = fifo.getItemToPop();
current_task = la;
cs.leave();
if(la)la->invoke(); // static_cast<TBase*>(this));
}
private:
typedef VdelegateFIFO_T<void(), Size_w> T_Fifo;
T_Fifo fifo;
typename T_Fifo::ItemBase* current_task;
const SST::TPriority priority;
};
Мжно использовать из любого уровня приоритта, в том числе из прерываний.
Как пользоваться этим я уже показывал ранее(пример стирания флешки, шина SPI)
Цитата
Тема про SST интересная. Не панацея, конечно, но попробовать можно. Жаль, что brag не привёл какого-нибудь полного примера своей реализации.
Не панацея, но это другой стиль и он имеет много преимуществ перед синхронным, я их всех уже разжевал в этой теме и показал на примерах. Примеров кода в качестве контр-аргументов не увидел. Увижу - попытаюсь сделать из них SST-вариант, посмотрим на какой платформе будет круче и проще

Полный пример приводить смысла нет - все равно не разберетесь без детального изучения и практики асинхронного программирования, да и так примеров я тут кучу привел, реально работающих,
во всех разобрались и поняли как работает?. Мое ядро ССТ ничем не отличается от того, что по ссылке, только сделано на плюсах и заточено под мои прихоти. Но если что-то конкретное интересует - спрашивайте, покажу.
Готового решения у меня нет, если хотите работать на SST - придется реализовывать самому, там очень мало строк кода. Если не сможете реализовать, значит не сможете работать и на готовой реализации.
Если все равно есть желание - практикуйтесь на NodeJS, там все уже готово, и есть отличная документация и куча инфы в интернете. Но там 1 приоритет, но это даже хорошо - научитесь работать в одном приоритете - потом сможете освоить и много-приоритетный стиль и движок свой напишете за пару дней.