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