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