The impleÂmentation represents a mutex as two values: a user-level key key and a semaphore sema. The key counts the number of processes interested in holding the lock, including the one that does hold it:
If key is 0, the mutex is unlocked
If key is 1, the mutex is locked
If key is larger than 1, the mutex is locked by one process and there are key-1 processes waiting to acquire it. Those processes wait on the semaphore sema
The fast path (uncontended case) allows acquiring the mutex entirely in user space, without the need to acquire a semaphore, thus avoiding a system call and a possible context switch
This implementation enforces the FIFO policy due to the internal implementation of runtime.Semacquire and runtime.Semrelease based on a queue. If multiple goroutines are blocked on sema trying to acquire the mutex (key is greater than 1), then after calling Unlock(), the first goroutine blocked on sema will acquire the mutex. This means that a newcomer goroutine will acquire the lock only after all the blocked goroutines
Allow Successive Acquisition
This implementation allows a goroutine to do successive acquisitions of a mutex even if there are blocked goroutines. Moreover, it allows a newcomer goroutine to acquire a mutex ahead of blocked goroutines (that is, it does not enforce FIFO)
On implementation level it’s achieved by separating waiter count and locked flag
This implementation uses a state field instead of a key to separately track whether a mutex is locked (0th bit) and by how many processes (2nd bit and up). The 1st bit is used to track whether a goroutine is woken up
Previous implementations are fully cooperative. That is, once contention is discovered, the goroutine calls into scheduler. This is suboptimal as the resource can become free soon after (especially if critical sections are short). Server software usually runs at ~50% CPU utilization, that is, switching to other goroutines is not necessary profitable.
This change adds limited active spinning if:
Running on a multicore machine and
GOMAXPROCS > 1 and
There is at least one other running ProcessorP and