1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package sync provides basic synchronization primitives such as mutual 6 // exclusion locks. Other than the Once and WaitGroup types, most are intended 7 // for use by low-level library routines. Higher-level synchronization is 8 // better done via channels and communication. 9 // 10 // Values containing the types defined in this package should not be copied. 11 package sync 12 13 import ( 14 "sync/atomic" 15 "unsafe" 16 ) 17 18 // A Mutex is a mutual exclusion lock. 19 // Mutexes can be created as part of other structures; 20 // the zero value for a Mutex is an unlocked mutex. 21 type Mutex struct { 22 state int32 23 sema uint32 24 } 25 26 // A Locker represents an object that can be locked and unlocked. 27 type Locker interface { 28 Lock() 29 Unlock() 30 } 31 32 const ( 33 mutexLocked = 1 << iota // mutex is locked 34 mutexWoken 35 mutexWaiterShift = iota 36 ) 37 38 // Lock locks m. 39 // If the lock is already in use, the calling goroutine 40 // blocks until the mutex is available. 41 func (m *Mutex) Lock() { 42 // Fast path: grab unlocked mutex. 43 if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { 44 if raceenabled { 45 raceAcquire(unsafe.Pointer(m)) 46 } 47 return 48 } 49 50 awoke := false 51 iter := 0 52 for { 53 old := m.state 54 new := old | mutexLocked 55 if old&mutexLocked != 0 { 56 if runtime_canSpin(iter) { 57 // Active spinning makes sense. 58 // Try to set mutexWoken flag to inform Unlock 59 // to not wake other blocked goroutines. 60 if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 && 61 atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) { 62 awoke = true 63 } 64 runtime_doSpin() 65 iter++ 66 continue 67 } 68 new = old + 1<<mutexWaiterShift 69 } 70 if awoke { 71 // The goroutine has been woken from sleep, 72 // so we need to reset the flag in either case. 73 if new&mutexWoken == 0 { 74 panic("sync: inconsistent mutex state") 75 } 76 new &^= mutexWoken 77 } 78 if atomic.CompareAndSwapInt32(&m.state, old, new) { 79 if old&mutexLocked == 0 { 80 break 81 } 82 runtime_Semacquire(&m.sema) 83 awoke = true 84 iter = 0 85 } 86 } 87 88 if raceenabled { 89 raceAcquire(unsafe.Pointer(m)) 90 } 91 } 92 93 // Unlock unlocks m. 94 // It is a run-time error if m is not locked on entry to Unlock. 95 // 96 // A locked Mutex is not associated with a particular goroutine. 97 // It is allowed for one goroutine to lock a Mutex and then 98 // arrange for another goroutine to unlock it. 99 func (m *Mutex) Unlock() { 100 if raceenabled { 101 _ = m.state 102 raceRelease(unsafe.Pointer(m)) 103 } 104 105 // Fast path: drop lock bit. 106 new := atomic.AddInt32(&m.state, -mutexLocked) 107 if (new+mutexLocked)&mutexLocked == 0 { 108 panic("sync: unlock of unlocked mutex") 109 } 110 111 old := new 112 for { 113 // If there are no waiters or a goroutine has already 114 // been woken or grabbed the lock, no need to wake anyone. 115 if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 { 116 return 117 } 118 // Grab the right to wake someone. 119 new = (old - 1<<mutexWaiterShift) | mutexWoken 120 if atomic.CompareAndSwapInt32(&m.state, old, new) { 121 runtime_Semrelease(&m.sema) 122 return 123 } 124 old = m.state 125 } 126 } 127