A Wait-Free Queue as Fast as Fetch-And-Add [pdf]
chaoran.me
chaoran.me
A wait-free queue for multiple consumers and multiple producers would be kind of the holy grail when it comes to wait free data structures. Is it MPMC though? I skipped through the PDF and could not find a definitive claim…
Also, big thumbs up to Chaoran for putting the implementation on GitHub: https://github.com/chaoran/fast-wait-free-queue The code looks like it is easy to follow and it's MIT licensed.
He previously saved my bacon with `node-finish` - a Node.JS library that let's you fire off a bunch of tasks, and wait for _all_ to finish before executing a callback.
What a hero.
There was a time before Promises, you know.
Even before Promises, wrapping up some callback functions and tracking async task state isn't rocket surgery.
> OP said "recently".
I did? I think you misread my "previously".Regardless, I was new to JS and not up to the task of implementing it myself. @chaoran's library did exactly what I needed.
An appreciative note didn't need to turn into criticism of the project's worth.
In spite of this, all the literature about lock-free queues that I see (including this one) is for some kind of linked structure. Any reason for that?
I guess one big limitation of the ring buffer is that it only works for a single reader and single writer. Although maybe you can work around that using CAS primitves...
http://rethinkdb.com/blog/lock-free-vs-wait-free-concurrency...
This might be relevant to answering your question.
Specifically does the every thread requirement rule out algorithms where there only one reader and one writer is allowed? If not, then this ring buffer is wait-free, since there is no CAS-and-retry step. (Of course an operation can fail because the queue is empty/full, but that is still completion).
* multiple/single producer, multiple/single consumer
* progress guarantees (wait-free, lock-free)
* bounded vs. unbounded
* whether it's double-ended or single-ended
* memory reclamation being dependant on a GC or not
* which atomic operations they need (e.g. some require double-CAS)
What you mention is a spsc bounded queue. this is about a mpmc unbounded queue. And from skimming the paper is seems like it does not require a GC either.
So it's far more powerful.
Of course spsc queues still have their use even if we have a good mpmc queue, they generally incur less overhead.
And indeed it you want all readers to get all elements, I think a ring buffer is easier than a linked structure.
If your queue is filling up arbitrarily far, then you are almost certainly in trouble anyway, and want some limitation strategy. The ring buffer gives you this quite naturally.
I suppose there could be cases where you did not want to pre-commit to a buffer size. E.g. you have lots of queues which you expect to be short, but which might grow large in rare cases. But I have never actually seen such a case.
I don't know Erlang or Akka first hand, but I know that Go channels are inpsired by Erlang. Channels can be very numerous and are usually empty; but they are fixed size. In the rare occasion that you think a channel needs more buffering, you must say so explicilty.
For practical examples of all of the above, see Boost's lockfree structures: http://www.boost.org/doc/libs/1_61_0/doc/html/lockfree.html
The double wait-free queue from the paper is using a technicality to achieve its status. The spinning operation is replaced with a loop over all the threads doing opposite (multi-step) operations, finishing them for threads that may be blocked in the middle of the steps. It's not that it doesn't spin, but the spinning is bounded by the number of threads. Edit: Actually, spinning is bounded by O(#threads^4) according to the paper.
I've been slipping it into systems for a couple years now :D
Which is what Rust mpsc uses. It has a big advantage in being short and easy to understand.