I'll start with the background.
So, I need an example of a "one writes - one reads" thread-safe C ++ queue. I searched on different sites and it seemed that something interesting had been dug up: here it is, this code .
But something further alerted me. More precisely, I did not understand something and began to search in various directories such as MSDN and cppreference, in different blogs, but alas. Turned here, namely here .
And now I have some doubt about that code. Yes, and I myself would have changed something, basically, I do not understand the purpose of the alloc_node() method there. No, I should not explain the logic of the algorithms from there, it seems to be quite obvious, and I understood it. Only now I think that two extra first and tail_copy pointers have been entered there, and this method is not needed, and the “writer” should be rewritten: the fact is that, apparently, his data are considered to be a reader, but they will hang in memory until destroyed by the destructor or overwritten. This would be useful if they would need to be seen again, but in such a situation, I think, a doubly linked list or ring, or at least a key library was required - and in that code there is no, there is only a next pointer at each node, and this is not enough. To create a special designator, constantly showing the end of the queue, and then passing through it, is also not very good, in my opinion.
I do not pretend to be a connoisseur, I am a novice, do not chase me, but it seems to me that it would be better (I haven’t figured out the barriers yet, if they are needed here). This is not a question, more thought out loud, it may be necessary to answer / someone will come in handy (if it works at all):
~spsc_queue() { if(load_consume(&tail) == nullptr) return; // если уже всё удалено до if(load_consume(&tail->next) == nullptr) { delete tail; return; } // если есть только начальное звено от конструктора do { node * next = tail->next; delete tail; store_release(&tail, next); } while(load_consume(&tail) != nullptr); } // другой поток (читатель) обязательно должен понимать, // что сначала должно быть записано значение в ячейку, // а уже потом эта ячейка станет для него видима, // то есть, она станет подключенной к очереди void enqueue(T v) { node* n = new node; n->next = nullptr; n->value = v; if(load_consume(&head) != nullptr) { // на всякий случай store_release(&head->next, n); } // тут подключается к очереди head = n; } // удаляется все считанное, кроме пустого начального звена, // который создаётся конструктором bool dequeue(T& v) { if(load_consume(&tail->next) != nullptr) { v = tail->next - > value; node * next = tail->next->next; delete tail->next; store_release(&tail->next, next); return true; } return false; } The writer uses only the head , the reader only the tail . To be honest, did not even check how it works. Just read articles from scrutator, and indeed, there was no time.
Well, finally, the long-awaited question: probably, in that code from the Intel site there are still some flaws or something like that. This is not obvious to me. I would like to know what else can be there, so that I can take this into account. Or, please, link to the sample code without blots and with the implementation of barriers and without depending on the platform. Or here. It is desirable that fits also under x64. Thank.