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