gmutex.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. // Package gmutex implements graceful concurrent-safe mutex with more rich features.
  7. package gmutex
  8. import (
  9. "math"
  10. "runtime"
  11. "github.com/gogf/gf/v2/container/gtype"
  12. )
  13. // Mutex is a high level Mutex, which implements more rich features for mutex.
  14. type Mutex struct {
  15. state *gtype.Int32 // Indicates the state of mutex. -1: writing locked; > 1 reading locked.
  16. writer *gtype.Int32 // Pending writer count.
  17. reader *gtype.Int32 // Pending reader count.
  18. writing chan struct{} // Channel for writer blocking.
  19. reading chan struct{} // Channel for reader blocking.
  20. }
  21. // New creates and returns a new mutex.
  22. func New() *Mutex {
  23. return &Mutex{
  24. state: gtype.NewInt32(),
  25. writer: gtype.NewInt32(),
  26. reader: gtype.NewInt32(),
  27. writing: make(chan struct{}, 1),
  28. reading: make(chan struct{}, math.MaxInt32),
  29. }
  30. }
  31. // Lock locks the mutex for writing purpose.
  32. // If the mutex is already locked by another goroutine for reading or writing,
  33. // it blocks until the lock is available.
  34. func (m *Mutex) Lock() {
  35. for {
  36. // Using CAS operation to get the writing lock atomically.
  37. if m.state.Cas(0, -1) {
  38. return
  39. }
  40. // It or else blocks to wait for the next chance.
  41. m.writer.Add(1)
  42. <-m.writing
  43. }
  44. }
  45. // Unlock unlocks writing lock on the mutex.
  46. // It is safe to be called multiple times even there's no locks.
  47. func (m *Mutex) Unlock() {
  48. if m.state.Cas(-1, 0) {
  49. // Note that there might be more than one goroutines can enter this block.
  50. var n int32
  51. // Writing lock unlocks, then first check the blocked readers.
  52. // If there are readers blocked, it unlocks them with preemption.
  53. for {
  54. if n = m.reader.Val(); n > 0 {
  55. if m.reader.Cas(n, 0) {
  56. for ; n > 0; n-- {
  57. m.reading <- struct{}{}
  58. }
  59. break
  60. } else {
  61. runtime.Gosched()
  62. }
  63. } else {
  64. break
  65. }
  66. }
  67. // It then also kindly feeds the pending writers with one chance.
  68. if n = m.writer.Val(); n > 0 {
  69. if m.writer.Cas(n, n-1) {
  70. m.writing <- struct{}{}
  71. }
  72. }
  73. }
  74. }
  75. // TryLock tries locking the mutex for writing purpose.
  76. // It returns true immediately if success, or if there's a write/reading lock on the mutex,
  77. // it returns false immediately.
  78. func (m *Mutex) TryLock() bool {
  79. return m.state.Cas(0, -1)
  80. }
  81. // RLock locks mutex for reading purpose.
  82. // If the mutex is already locked for writing,
  83. // it blocks until the lock is available.
  84. func (m *Mutex) RLock() {
  85. var n int32
  86. for {
  87. if n = m.state.Val(); n >= 0 {
  88. // If there's no writing lock currently, then do the reading lock checks.
  89. if m.state.Cas(n, n+1) {
  90. return
  91. } else {
  92. runtime.Gosched()
  93. }
  94. } else {
  95. // It or else pends the reader.
  96. m.reader.Add(1)
  97. <-m.reading
  98. }
  99. }
  100. }
  101. // RUnlock unlocks the reading lock on the mutex.
  102. // It is safe to be called multiple times even there's no locks.
  103. func (m *Mutex) RUnlock() {
  104. var n int32
  105. for {
  106. if n = m.state.Val(); n >= 1 {
  107. if m.state.Cas(n, n-1) {
  108. break
  109. } else {
  110. runtime.Gosched()
  111. }
  112. } else {
  113. break
  114. }
  115. }
  116. // Reading lock unlocks, it then only check the blocked writers.
  117. // Note that it is not necessary to check the pending readers here.
  118. // `n == 1` means the state of mutex comes down to zero.
  119. if n == 1 {
  120. if n = m.writer.Val(); n > 0 {
  121. if m.writer.Cas(n, n-1) {
  122. m.writing <- struct{}{}
  123. }
  124. }
  125. }
  126. }
  127. // TryRLock tries locking the mutex for reading purpose.
  128. // It returns true immediately if success, or if there's a writing lock on the mutex,
  129. // it returns false immediately.
  130. func (m *Mutex) TryRLock() bool {
  131. var n int32
  132. for {
  133. if n = m.state.Val(); n >= 0 {
  134. if m.state.Cas(n, n+1) {
  135. return true
  136. } else {
  137. runtime.Gosched()
  138. }
  139. } else {
  140. return false
  141. }
  142. }
  143. }
  144. // IsLocked checks whether the mutex is locked with writing or reading lock.
  145. // Note that the result might be changed after it's called,
  146. // so it cannot be the criterion for atomic operations.
  147. func (m *Mutex) IsLocked() bool {
  148. return m.state.Val() != 0
  149. }
  150. // IsWLocked checks whether the mutex is locked by writing lock.
  151. // Note that the result might be changed after it's called,
  152. // so it cannot be the criterion for atomic operations.
  153. func (m *Mutex) IsWLocked() bool {
  154. return m.state.Val() < 0
  155. }
  156. // IsRLocked checks whether the mutex is locked by reading lock.
  157. // Note that the result might be changed after it's called,
  158. // so it cannot be the criterion for atomic operations.
  159. func (m *Mutex) IsRLocked() bool {
  160. return m.state.Val() > 0
  161. }
  162. // LockFunc locks the mutex for writing with given callback function `f`.
  163. // If there's a write/reading lock the mutex, it will blocks until the lock is released.
  164. //
  165. // It releases the lock after `f` is executed.
  166. func (m *Mutex) LockFunc(f func()) {
  167. m.Lock()
  168. defer m.Unlock()
  169. f()
  170. }
  171. // RLockFunc locks the mutex for reading with given callback function `f`.
  172. // If there's a writing lock the mutex, it will blocks until the lock is released.
  173. //
  174. // It releases the lock after `f` is executed.
  175. func (m *Mutex) RLockFunc(f func()) {
  176. m.RLock()
  177. defer m.RUnlock()
  178. f()
  179. }
  180. // TryLockFunc tries locking the mutex for writing with given callback function `f`.
  181. // it returns true immediately if success, or if there's a write/reading lock on the mutex,
  182. // it returns false immediately.
  183. //
  184. // It releases the lock after `f` is executed.
  185. func (m *Mutex) TryLockFunc(f func()) (result bool) {
  186. if m.TryLock() {
  187. result = true
  188. defer m.Unlock()
  189. f()
  190. }
  191. return
  192. }
  193. // TryRLockFunc tries locking the mutex for reading with given callback function `f`.
  194. // It returns true immediately if success, or if there's a writing lock on the mutex,
  195. // it returns false immediately.
  196. //
  197. // It releases the lock after `f` is executed.
  198. func (m *Mutex) TryRLockFunc(f func()) (result bool) {
  199. if m.TryRLock() {
  200. result = true
  201. defer m.RUnlock()
  202. f()
  203. }
  204. return
  205. }