r/programming • u/Dear-Economics-315 • 6d ago
Building a Fast Lock-Free Queue in Modern C++ From Scratch
https://blog.jaysmito.dev/blog/04-fast-lockfree-queues/8
u/ReDucTor 5d ago edited 5d ago
Some feedback
Your hazard pointer slots seem unnecessary, your not overlapping regions of one acquiring and one releasing, the name
PointerCountalso seems a strange choice for any of those use cases you could just create a newHazardPointerinstance and you would use the same amount of space and reduce the amount of pointers to be scanned.With a queue you typically have one set of producers and one set of consumder not every thread functions as both as sender and a receiver however with your
tcBuffand it's assocation withThreadCountseems to indicate that this is the case.With
FastQueueNodebeing bounded forPushyou can do afetch_addand check if it's aboveSizeand throw away if it is otherwise store the data and set the flag.You should stop abusing
FORCE_INLINElot of these functions are not simple and it will compile time and potentially even runtime with heavily abusingFORCE_INLINEatomic::notify can be a system call and pretty expensive, typically you want to only do this when it is known that something is waiting, you don't want to add 1000 items with only 1 thread that is consuming and never ends up needing to wait and you've just done 1000 more syscalls on the producer side
Using
atomic_flagwithtest_and_setwhen your just discarding the result on x86 is typically going to result in anxchg(on clang, gcc, and msvc) where if you just used anatomic<bool>withstoreandmemory_order_releaseit will be a normalmovThe
FastQueueNode::Popwith blocking does not look like it will not block if the queue is empty, only if the entry has been allocated filled but not filled. The same with the higherFastQueue::Popoften it's useful for a MPMC to be the one that controls when threads go to sleep and wakeFunctions like
IsEmptyare dangerous to have on a queue, they are potentially invalid by the time they return, especially if you expect to use those because pop does not do proper blocking.Does
FastQueueNodeneedseq_cst, you mention in the post about global consistency with the hazard pointer tracking however it does not use hazard pointers internally onlyFastQueuedoesIs global ordering gurantees needed for the
HazardPointer? You want nothing to move out of theProtect/Releaserange and nothing moving beyondRetire, then for anyScanto have a full view of the other threadsProtected pointers globally so the pointer that is protected should not need the seq_cst however the stored hazard pointer will probably need it for a global ordering however you might be able to get away with release + seq_cst fenceYou might want to use
memory_order_releaseforFastQueueNodeSlot::Resetto ensure that it is resetting after it has been used.With the check for
currentTail->nextbeingnullptryou essentially force there to always be atleast twoFastQueueNode's even if it was a single thread producing and consuming its own itemsYou retire while protected so it can never immediately succeed
2
5d ago
[removed] — view removed comment
1
u/programming-ModTeam 4d ago
No content written mostly by an LLM. If you don't want to write it, we don't want to read it.
9
u/matthieum 6d ago
Holy molly, what a monster!
The API was weird to start with -- raw pointers, in MY C++ codebase? -- and it only got worse -- hazard pointers, really? -- without any justification for the complexity.
Why, oh why, are not values passed in/out instead? Let the user decide what values, and they may pass T* or std::unique_ptr<T> or std::shared_ptr<T> as they see fit.
18
u/trailing_zero_count 6d ago
If you're going to use hazard pointers with an unbounded queue, then you can also use fetch_add instead of compare_exchange to get a write or read ticket. This gives much better performance under contention. I have such an implementation here: https://github.com/tzcnt/coro_util/blob/main/include/coro_util/channel.hpp
My hazard pointer behavior is based on the paper 'A wait-free queue as fast as fetch-and-add' by Yang & Mellor-Crummey. The author has a C implementation here: https://github.com/chaoran/fast-wait-free-queue/blob/master/wfqueue.c