Source file src/pkg/sync/mutex.go
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 "sync/atomic" 14 15 // A Mutex is a mutual exclusion lock. 16 // Mutexes can be created as part of other structures; 17 // the zero value for a Mutex is an unlocked mutex. 18 type Mutex struct { 19 state int32 20 sema uint32 21 } 22 23 // A Locker represents an object that can be locked and unlocked. 24 type Locker interface { 25 Lock() 26 Unlock() 27 } 28 29 const ( 30 mutexLocked = 1 << iota // mutex is locked 31 mutexWoken 32 mutexWaiterShift = iota 33 ) 34 35 // Lock locks m. 36 // If the lock is already in use, the calling goroutine 37 // blocks until the mutex is available. 38 func (m *Mutex) Lock() { 39 // Fast path: grab unlocked mutex. 40 if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { 41 return 42 } 43 44 awoke := false 45 for { 46 old := m.state 47 new := old | mutexLocked 48 if old&mutexLocked != 0 { 49 new = old + 1<<mutexWaiterShift 50 } 51 if awoke { 52 // The goroutine has been woken from sleep, 53 // so we need to reset the flag in either case. 54 new &^= mutexWoken 55 } 56 if atomic.CompareAndSwapInt32(&m.state, old, new) { 57 if old&mutexLocked == 0 { 58 break 59 } 60 runtime_Semacquire(&m.sema) 61 awoke = true 62 } 63 } 64 } 65 66 // Unlock unlocks m. 67 // It is a run-time error if m is not locked on entry to Unlock. 68 // 69 // A locked Mutex is not associated with a particular goroutine. 70 // It is allowed for one goroutine to lock a Mutex and then 71 // arrange for another goroutine to unlock it. 72 func (m *Mutex) Unlock() { 73 // Fast path: drop lock bit. 74 new := atomic.AddInt32(&m.state, -mutexLocked) 75 if (new+mutexLocked)&mutexLocked == 0 { 76 panic("sync: unlock of unlocked mutex") 77 } 78 79 old := new 80 for { 81 // If there are no waiters or a goroutine has already 82 // been woken or grabbed the lock, no need to wake anyone. 83 if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 { 84 return 85 } 86 // Grab the right to wake someone. 87 new = (old - 1<<mutexWaiterShift) | mutexWoken 88 if atomic.CompareAndSwapInt32(&m.state, old, new) { 89 runtime_Semrelease(&m.sema) 90 return 91 } 92 old = m.state 93 } 94 }