Spinlocks are great way of telling the kernel that you believe in small governments, and no regulations. I mean really, why should the kernel meddle in your user business?
One neat thing is, you don't actually need a spinlock if you're using a ringbuffer. You can use atomic increment on a counter to claim a slot, then write to that slot.<p>It can be between two to three orders of magnitude higher throughput. Equally important, lower latency.<p>(Throughput tends to be a consequence of low latency, but not always.)<p>I'm not saying this should be the norm, though. You probably don't need this design. But when you do, e.g. processing millions of stock market messages, there's no substitute.<p>EDIT: I love hearing about the designs and questions, but it was probably a mistake for me not to be explicit. Sorry! The thing I'm referring to is LMAX Disruptor pattern: <a href="https://lmax-exchange.github.io/disruptor/" rel="nofollow">https://lmax-exchange.github.io/disruptor/</a><p>I learned about it in 2011-ish, and it deeply changed my perspective on high speed designs.
Notice that this code probably has a bug. [Edited to add: spacechild1 says it doesn't, and I now believe them]<p>It keeps using exchange() which swaps the old value in memory for your value and give back the old value, but it sets std::memory_order_acquire with the author apparently thinking that since this wants to <i>acquire</i> a lock this is enough.<p>But it isn't. The exchange() call is <i>two</i> memory operations, it's a load <i>and</i> a store, and so what you wanted here was Acquire <i>and</i> Release semantics ie. memory_order::acq_rel<p>What has been written is effectively Relaxed semantics for the store, and C++ doesn't do a great job of explaining that to programmers.
The debate about whether further reordering is possible is an interesting one. I think it's evidence that it's basically not possible for humans to reason about the memory model. The best way to resolve it is to use an automated checking tool based on a formal model. Good choices are CDSChecker[1] or the newer CppMem[2].<p>[1]: <a href="http://plrg.ics.uci.edu/software_page/42-2/" rel="nofollow">http://plrg.ics.uci.edu/software_page/42-2/</a><p>[2]: <a href="http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/help.html" rel="nofollow">http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/help.html</a>
Multithreading sure is complicated.<p>I'm currently playing with multithreading right now. I'm implementing snapshot isolation multiversion concurrency control.<p>In theory you can avoid locks (except for data structure locks) by creating a copy of the data you want to write and detect conflicts at read and commit time. Set the read timestamp of a piece of data to the transaction timestamp (timestamps are just monotonically increasing numbers) that reads it. If someone with a higher transaction timestamp comes along, they abort and restart because someone got in before them.<p>At the moment I have something that mostly works but occasionally executes a duplicate. I'm trying to eradicate the last source of bugs but as with anything parallel, it's complicated due to the interleavings.<p>My test case is to spin up 100 threads, with each thread trying to increment a number. The end numbers should be 101 and 102. If there was a data race, then the numbers will be lower.<p><a href="https://github.com/samsquire/multiversion-concurrency-control" rel="nofollow">https://github.com/samsquire/multiversion-concurrency-contro...</a>
For a real battle-tested combination of spinlock and futex, see <a href="https://github.com/abseil/abseil-cpp/blob/master/absl/synchronization/mutex.cc" rel="nofollow">https://github.com/abseil/abseil-cpp/blob/master/absl/synchr...</a>
I think it may become even easier with C++20 and the addition of wait/notify to std::atomic<p><a href="https://en.cppreference.com/w/cpp/atomic/atomic/wait" rel="nofollow">https://en.cppreference.com/w/cpp/atomic/atomic/wait</a>
Regarding moving loads and stores into acq/rel critical sections, this is possible, well studied and documented; it is known roach motel reordering since the Java MM standardisation, and generally considered safe.<p>I never thought whether it could lead to deadlocks or not and it is an interesting question. I assume there musy ve some formal proof against it but I would have to think about it (my first hunch is that if the reordering could cause a deadlock, then the cose wasn't safe in any case, like not acquiring locks in a consistent order).
I'm surprised nobody has mentioned Linus's rant about spinlocks in userspace: <a href="https://www.realworldtech.com/forum/?threadid=189711&curpostid=189752" rel="nofollow">https://www.realworldtech.com/forum/?threadid=189711&curpost...</a>