github.com/euank/go@v0.0.0-20160829210321-495514729181/src/sync/mutex.go (about) 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 "internal/race" 15 "sync/atomic" 16 "unsafe" 17 ) 18 19 // A Mutex is a mutual exclusion lock. 20 // Mutexes can be created as part of other structures; 21 // the zero value for a Mutex is an unlocked mutex. 22 // 23 // A Mutex must not be copied after first use. 24 type Mutex struct { 25 state int32 26 sema uint32 27 } 28 29 // A Locker represents an object that can be locked and unlocked. 30 type Locker interface { 31 Lock() 32 Unlock() 33 } 34 35 const ( 36 mutexLocked = 1 << iota // mutex is locked 37 mutexWoken 38 mutexWaiterShift = iota 39 ) 40 41 // Lock locks m. 42 // If the lock is already in use, the calling goroutine 43 // blocks until the mutex is available. 44 func (m *Mutex) Lock() { 45 // Fast path: grab unlocked mutex. 46 if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { 47 if race.Enabled { 48 race.Acquire(unsafe.Pointer(m)) 49 } 50 return 51 } 52 53 awoke := false 54 iter := 0 55 for { 56 old := m.state 57 new := old | mutexLocked 58 if old&mutexLocked != 0 { 59 if runtime_canSpin(iter) { 60 // Active spinning makes sense. 61 // Try to set mutexWoken flag to inform Unlock 62 // to not wake other blocked goroutines. 63 if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 && 64 atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) { 65 awoke = true 66 } 67 runtime_doSpin() 68 iter++ 69 continue 70 } 71 new = old + 1<<mutexWaiterShift 72 } 73 if awoke { 74 // The goroutine has been woken from sleep, 75 // so we need to reset the flag in either case. 76 if new&mutexWoken == 0 { 77 panic("sync: inconsistent mutex state") 78 } 79 new &^= mutexWoken 80 } 81 if atomic.CompareAndSwapInt32(&m.state, old, new) { 82 if old&mutexLocked == 0 { 83 break 84 } 85 runtime_Semacquire(&m.sema) 86 awoke = true 87 iter = 0 88 } 89 } 90 91 if race.Enabled { 92 race.Acquire(unsafe.Pointer(m)) 93 } 94 } 95 96 // Unlock unlocks m. 97 // It is a run-time error if m is not locked on entry to Unlock. 98 // 99 // A locked Mutex is not associated with a particular goroutine. 100 // It is allowed for one goroutine to lock a Mutex and then 101 // arrange for another goroutine to unlock it. 102 func (m *Mutex) Unlock() { 103 if race.Enabled { 104 _ = m.state 105 race.Release(unsafe.Pointer(m)) 106 } 107 108 // Fast path: drop lock bit. 109 new := atomic.AddInt32(&m.state, -mutexLocked) 110 if (new+mutexLocked)&mutexLocked == 0 { 111 panic("sync: unlock of unlocked mutex") 112 } 113 114 old := new 115 for { 116 // If there are no waiters or a goroutine has already 117 // been woken or grabbed the lock, no need to wake anyone. 118 if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 { 119 return 120 } 121 // Grab the right to wake someone. 122 new = (old - 1<<mutexWaiterShift) | mutexWoken 123 if atomic.CompareAndSwapInt32(&m.state, old, new) { 124 runtime_Semrelease(&m.sema) 125 return 126 } 127 old = m.state 128 } 129 }