HNNewShowAskJobs
Built with Tanstack Start
Spinning around: Please don't – Common problems with spin locks(siliceum.com)
84 points by bdash 10 hours ago | 30 comments
  • pizlonator4 hours ago

    TFA lists WebKit as a project that "does it wrong".

    The author should read https://webkit.org/blog/6161/locking-in-webkit/ so that they understand what they are talking about.

    WebKit does it right in the sense that:

    - It as an optimal amount of spinning

    - Threads wait (instead of spinning) if the lock is not available immediately-ish

    And we know that the algorithms are optimal based on rigorous experiments.

  • jcranmer7 hours ago

    The basic rule of writing your own cross-thread datastructures like mutexes or condition variables is... don't, unless you have very good reason not to. If you're in that rare circumstance where you know the library you're using isn't viable for some reason, then the next best rule is to use your OS's version of a futex as the atomic primitive, since it's going to solve most of the pitfalls for you automatically.

    The only time I've manually written my own spin lock was when I had to coordinate between two different threads, one of which was running 16-bit code, so using any library was out of the question, and even relying on syscalls was sketchy because making sure the 16-bit code is in the right state to call a syscall itself is tricky. Although in this case, since I didn't need to care about things like fairness (only two threads are involved), the spinlock core ended up being simple:

        "thunk_spin:",
            "xchg cx, es:[{in_rv}]",
            "test cx, cx",
            "jnz thunk_has_data",
            "pause",
            "jmp thunk_spin",
        "thunk_has_data:",
    • fasterik7 hours ago |parent

      As always: use standard libraries first, profile, then write your own if the data indicate that it's necessary. To your point, the standard library probably already uses the OS primitives under the hood, which themselves do a short userspace spin-wait and then fall back to a kernel wait queue on contention. If low latency is a priority, the latter might be unacceptable.

      The following is an interesting talk where the author used a custom spinlock to significantly speed up a real-time physics solver.

      Dennis Gustafsson – Parallelizing the physics solver – BSC 2025 https://www.youtube.com/watch?v=Kvsvd67XUKw

    • kccqzy7 hours ago |parent

      Another time when writing a quick and dirty spinlock is reasonable is inside a logging library. A logging library would normally use a full-featured mutex, but what if we want the mutex implementation to be able to log? Say the mutex can log that it is non recursive yet the same thread is acquiring it twice; or that it has detected a deadlock. The solution is to introduce a special subset of the logging library to use a spinlock.

      • wizzwizz46 hours ago |parent

        I'm not sure how a spinlock solves this problem. Wouldn't that just cause the process to hang busy?

        • direwolf206 hours ago |parent

          Only until the other thread leaves the logger

          • wizzwizz45 hours ago |parent

            Oh, I see: the spinlock is for logging the deadlocks of other mutices, not for magically remediating deadlocks.

    • wallstop5 hours ago |parent

      I wrote my own spin lock library over a decade ago in order to learn about multi threading, concurrency, and how all this stuff works. I learned a lot!

    • squirrellous2 hours ago |parent

      Another somewhat known case of a spinlock is in trading, where for latency purposes the OS scheduler is essentially bypassed by core isolation and thread pinning, so there’s nothing better for the CPU to do than spinning.

  • spacechild13 hours ago

    Nice article! Yes, using spinlocks in normal userspace applications is not recommended.

    One area where I found spinlocks to be useful is in multithreaded audio applications. Audio threads are not supposed to be preempted by other user space threads because otherwise they may not complete in time, leading to audio glitches. The threads have a very high priority (or have a special scheduling policy) and may be pinned to different CPU cores.

    For example, multiple audio threads might read from the same sample buffer, whose content is occasionally modified. In that case, you could use a reader-writer-spinlock where multiple readers would be able to progress in parallel without blocking each other. Only a writer would block other threads.

    What would be the potential problems in that scenario?

  • horizion2025an hour ago

    My concurrency knowledge is a bit rusty but aren't spinlocks only supposed to be used for very brief waits like in the hundreds of cycles (or situations where you can't block... like internal o/s scheduling structures in SMP setups)? If so how much does all this back off and starvation of higher priority threads even matter? If it is longer then you should use a locking primitive (except for in those low level os structures!) where most of the things discussed are not an issue. Would love to hear the use cases where spin locks are needed in eg user space, I dont doubt they occur.

  • fsckboyan hour ago

    what is/are the thread synchronization protocol called which is the equivalent to ethernet's CSMA? there's no "carrier sensing", but instead "who won or mistakes were made" sensing. or is that just considered a form of spinlock? (you're not waiting for a lock, you perform your operation then see if it worked; though you could make the operation be "acquire lock" in which case it's a spinlock)

  • rdtsc5 hours ago

    > Notice that in the Skylake Client microarchitecture the RDTSC instruction counts at the machine’s guaranteed P1 frequency independently of the current processor clock (see the INVARIANT TSC property), and therefore, when running in Intel® Turbo-Boost-enabled mode, the delay will remain constant, but the number of instructions that could have been executed will change.

    rdtsc may execute out of order, so sometimes an lfence (previously cpuid) can be used and there is also rdtscp

    See https://github.com/torvalds/linux/blob/master/arch/x86/inclu...

    And just because rdtsc is constant doesn't mean the processor clock will be constant that could be fluctuating.

  • a-dub5 hours ago

    i always got the sense that spinlocks were about maximum portability and reliability in the face of unreliable event driven approaches. the dumb inefficient thing that makes the heads of the inexperienced explode, but actually just works and makes the world go 'round.

  • CamperBob28 hours ago

    Sheesh. Can something this complicated ever truly be said to work?

    • adrr6 hours ago |parent

      OS kernel runqueue is using a spinlock to schedule everything. So it works. Should you ever use a spinlock in application code? No. Let the OS via the synchronization primitives in whatever language your app is in.

    • bluGill7 hours ago |parent

      You can limit yourself to the performance of a 1mhz 6502 with no OS if you don't like it. Even MSDos on a 8086 with 640K ram allows for things that require complexity of this type (not spin locks, but the tricks needed to make "terminate stay resident" work are evil in a similar way)

      • yjftsjthsd-h7 hours ago |parent

        I don't think that's fair. You can go fast, just not more than one task at a time.

        • bluGill7 hours ago |parent

          Modern CPUs (since around 2000) go faster in large part because they have multiple cores that can do more than one thing in a time. If your program needs to go faster using more cores is often your best answer and then you will need these tricks. (SIMD or the GPU are also common answers that might or might not be better for your problem)

          • yjftsjthsd-h7 hours ago |parent

            Modern CPUs can do 4-5 GHz singled threaded. (Sometimes you can even get a higher clock speed by disabling other cores.) This somewhat outpaces "a 1mhz 6502" even without parallelization.

            • bluGill7 hours ago |parent

              They can, but nobody runs a single process on such CPUs. They run some form of OS which implements spinlock, mutexes, and all these other complex things.

              I suppose someplace someone is running an embedded system without an OS on such a processor - but I'd expect they are still using extra cores and so have all of the above tricks someplace.

    • direwolf206 hours ago |parent

      Yes, if you're careful. Actually careful, not pretend careful. Which is pretty normal in C and C++.

    • nh23423fefe7 hours ago |parent

      Isn't it the opposite? The complication is evidence of function. The simple code doesn't work.

      • kelnos7 hours ago |parent

        That assertion feels suspiciously like a logical fallacy.

        • pixl973 hours ago |parent

          Not really. A different place to look for this is in chemical reactions and things biological life does.

          You may have some simple chemical life needs, and life may have some other simple chemical it can use to get the needed simple chemical, but the processing steps are complex and limited by physics themselves. Evolution almost always finds a path of using the minimum activation energy to let these reactions occur. Trying to make the process simpler just doesn't get you what you need.

        • maxbond6 hours ago |parent

          Not really. If the solution has less complexity than is inherent in the problem, it can't possibly work. If the solution has complexity equal to or greater than the complexity inherent in the problem, it may work. So if you see complex code handling many different edge cases, you can take that as an indicator the author understood the problem. That doesn't mean they do understand or that the solution does work; only that you have more confidence than you did initially.

          It's a weak signal but the reasoning is sound.

          • pyrolistical6 hours ago |parent

            Everything should be made as simple as possible, but not simpler.

            Code has a minimum complexity to solve the problem

  • gafferongames8 hours ago

    Great article! Thanks for posting this.

  • jeffbee6 hours ago

    "Unfair" paragraph is way too short. This is the main problem! The outlier starvation you get from contended spinlocks is extraordinary and, hypothetically, unbounded.

    • tialaramex5 hours ago |parent

      Well, you need to have specified what you actually want. "Fair" sounds like it's just good, but it's expensive, so unless you know that you need it, which probably means knowing why, you probably don't want to pay the price.

      Stealing is an example of an unfairness which can significantly improve overall performance.