12 Atomics, spin locks, and futexes

 

This chapter covers

  • Synchronizing with atomic variables
  • Developing mutexes with spin locks
  • Improving spin locks with futexes

In previous chapters, we have used mutexes to synchronize access to shared variables amongst threads. We have also seen how to use mutexes as primitives to build more complex concurrent tools, such as semaphores and channels. We haven’t yet explored how these mutexes are built.

In this chapter, we’ll cover the most primitive of the synchronization tools: the atomic variable. We’ll then explore how we can use it to build a mutex using a technique called spin locking. Later, we’ll see how we can optimize the mutex implementation by making use of a futex—an operating system call allowing us to reduce the CPU cycles while waiting for a lock to become free. Finally, we’ll focus on how Go implements the bundled mutex.

12.1 Lock-free synchronization with atomic variables

Mutexes ensure that critical sections of our concurrent code are executed by only one goroutine at a time. They are used to prevent race conditions. However, mutexes have the effect of turning parts of our concurrent programming into sequential bottlenecks. If we are just updating the value of a simple variable, such as an integer, we can make use of an atomic variable to keep it consistent amongst goroutines without needing to rely on mutexes that turn our code into a sequential block.

12.1.1 Sharing variables with atomic numbers

12.1.2 Performance penalty when using atomics