| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package sync provides basic synchronization primitives such as mutual |
| // exclusion locks. Other than the Once and WaitGroup types, most are intended |
| // for use by low-level library routines. Higher-level synchronization is |
| // better done via channels and communication. |
| // |
| // Values containing the types defined in this package should not be copied. |
| package sync |
| |
| import ( |
| "sync/atomic" |
| "unsafe" |
| ) |
| |
| // A Mutex is a mutual exclusion lock. |
| // Mutexes can be created as part of other structures; |
| // the zero value for a Mutex is an unlocked mutex. |
| type Mutex struct { |
| state int32 |
| sema uint32 |
| } |
| |
| // A Locker represents an object that can be locked and unlocked. |
| type Locker interface { |
| Lock() |
| Unlock() |
| } |
| |
| const ( |
| mutexLocked = 1 << iota // mutex is locked |
| mutexWoken |
| mutexWaiterShift = iota |
| ) |
| |
| // Lock locks m. |
| // If the lock is already in use, the calling goroutine |
| // blocks until the mutex is available. |
| func (m *Mutex) Lock() { |
| // Fast path: grab unlocked mutex. |
| if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { |
| if raceenabled { |
| raceAcquire(unsafe.Pointer(m)) |
| } |
| return |
| } |
| |
| awoke := false |
| iter := 0 |
| for { |
| old := m.state |
| new := old | mutexLocked |
| if old&mutexLocked != 0 { |
| if runtime_canSpin(iter) { |
| // Active spinning makes sense. |
| // Try to set mutexWoken flag to inform Unlock |
| // to not wake other blocked goroutines. |
| if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 && |
| atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) { |
| awoke = true |
| } |
| runtime_doSpin() |
| iter++ |
| continue |
| } |
| new = old + 1<<mutexWaiterShift |
| } |
| if awoke { |
| // The goroutine has been woken from sleep, |
| // so we need to reset the flag in either case. |
| if new&mutexWoken == 0 { |
| panic("sync: inconsistent mutex state") |
| } |
| new &^= mutexWoken |
| } |
| if atomic.CompareAndSwapInt32(&m.state, old, new) { |
| if old&mutexLocked == 0 { |
| break |
| } |
| runtime_Semacquire(&m.sema) |
| awoke = true |
| iter = 0 |
| } |
| } |
| |
| if raceenabled { |
| raceAcquire(unsafe.Pointer(m)) |
| } |
| } |
| |
| // Unlock unlocks m. |
| // It is a run-time error if m is not locked on entry to Unlock. |
| // |
| // A locked Mutex is not associated with a particular goroutine. |
| // It is allowed for one goroutine to lock a Mutex and then |
| // arrange for another goroutine to unlock it. |
| func (m *Mutex) Unlock() { |
| if raceenabled { |
| _ = m.state |
| raceRelease(unsafe.Pointer(m)) |
| } |
| |
| // Fast path: drop lock bit. |
| new := atomic.AddInt32(&m.state, -mutexLocked) |
| if (new+mutexLocked)&mutexLocked == 0 { |
| panic("sync: unlock of unlocked mutex") |
| } |
| |
| old := new |
| for { |
| // If there are no waiters or a goroutine has already |
| // been woken or grabbed the lock, no need to wake anyone. |
| if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 { |
| return |
| } |
| // Grab the right to wake someone. |
| new = (old - 1<<mutexWaiterShift) | mutexWoken |
| if atomic.CompareAndSwapInt32(&m.state, old, new) { |
| runtime_Semrelease(&m.sema) |
| return |
| } |
| old = m.state |
| } |
| } |