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