mutex.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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. // Package sync provides basic synchronization primitives such as mutual
  5. // exclusion locks. Other than the Once and WaitGroup types, most are intended
  6. // for use by low-level library routines. Higher-level synchronization is
  7. // better done via channels and communication.
  8. //
  9. // Values containing the types defined in this package should not be copied.
  10. package sync
  11. import (
  12. "sync/atomic"
  13. "unsafe"
  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. // A Locker represents an object that can be locked and unlocked.
  23. type Locker interface {
  24. Lock()
  25. Unlock()
  26. }
  27. const (
  28. mutexLocked = 1 << iota // mutex is locked
  29. mutexWoken
  30. mutexWaiterShift = iota
  31. )
  32. // Lock locks m.
  33. // If the lock is already in use, the calling goroutine
  34. // blocks until the mutex is available.
  35. func (m *Mutex) Lock() {
  36. // Fast path: grab unlocked mutex.
  37. if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
  38. if raceenabled {
  39. raceAcquire(unsafe.Pointer(m))
  40. }
  41. return
  42. }
  43. awoke := false
  44. for {
  45. old := m.state
  46. new := old | mutexLocked
  47. if old&mutexLocked != 0 {
  48. new = old + 1<<mutexWaiterShift
  49. }
  50. if awoke {
  51. // The goroutine has been woken from sleep,
  52. // so we need to reset the flag in either case.
  53. new &^= mutexWoken
  54. }
  55. if atomic.CompareAndSwapInt32(&m.state, old, new) {
  56. if old&mutexLocked == 0 {
  57. break
  58. }
  59. runtime_Semacquire(&m.sema)
  60. awoke = true
  61. }
  62. }
  63. if raceenabled {
  64. raceAcquire(unsafe.Pointer(m))
  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. if raceenabled {
  75. _ = m.state
  76. raceRelease(unsafe.Pointer(m))
  77. }
  78. // Fast path: drop lock bit.
  79. new := atomic.AddInt32(&m.state, -mutexLocked)
  80. if (new+mutexLocked)&mutexLocked == 0 {
  81. panic("sync: unlock of unlocked mutex")
  82. }
  83. old := new
  84. for {
  85. // If there are no waiters or a goroutine has already
  86. // been woken or grabbed the lock, no need to wake anyone.
  87. if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {
  88. return
  89. }
  90. // Grab the right to wake someone.
  91. new = (old - 1<<mutexWaiterShift) | mutexWoken
  92. if atomic.CompareAndSwapInt32(&m.state, old, new) {
  93. runtime_Semrelease(&m.sema)
  94. return
  95. }
  96. old = m.state
  97. }
  98. }