gmutex.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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/container/gtype"
  12. )
  13. // The 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. if m.state.Cas(0, -1) {
  80. return true
  81. }
  82. return false
  83. }
  84. // RLock locks mutex for reading purpose.
  85. // If the mutex is already locked for writing,
  86. // it blocks until the lock is available.
  87. func (m *Mutex) RLock() {
  88. var n int32
  89. for {
  90. if n = m.state.Val(); n >= 0 {
  91. // If there's no writing lock currently, then do the reading lock checks.
  92. if m.state.Cas(n, n+1) {
  93. return
  94. } else {
  95. runtime.Gosched()
  96. }
  97. } else {
  98. // It or else pends the reader.
  99. m.reader.Add(1)
  100. <-m.reading
  101. }
  102. }
  103. }
  104. // RUnlock unlocks the reading lock on the mutex.
  105. // It is safe to be called multiple times even there's no locks.
  106. func (m *Mutex) RUnlock() {
  107. var n int32
  108. for {
  109. if n = m.state.Val(); n >= 1 {
  110. if m.state.Cas(n, n-1) {
  111. break
  112. } else {
  113. runtime.Gosched()
  114. }
  115. } else {
  116. break
  117. }
  118. }
  119. // Reading lock unlocks, it then only check the blocked writers.
  120. // Note that it is not necessary to check the pending readers here.
  121. // <n == 1> means the state of mutex comes down to zero.
  122. if n == 1 {
  123. if n = m.writer.Val(); n > 0 {
  124. if m.writer.Cas(n, n-1) {
  125. m.writing <- struct{}{}
  126. }
  127. }
  128. }
  129. }
  130. // TryRLock tries locking the mutex for reading purpose.
  131. // It returns true immediately if success, or if there's a writing lock on the mutex,
  132. // it returns false immediately.
  133. func (m *Mutex) TryRLock() bool {
  134. var n int32
  135. for {
  136. if n = m.state.Val(); n >= 0 {
  137. if m.state.Cas(n, n+1) {
  138. return true
  139. } else {
  140. runtime.Gosched()
  141. }
  142. } else {
  143. return false
  144. }
  145. }
  146. }
  147. // IsLocked checks whether the mutex is locked with writing or reading lock.
  148. // Note that the result might be changed after it's called,
  149. // so it cannot be the criterion for atomic operations.
  150. func (m *Mutex) IsLocked() bool {
  151. return m.state.Val() != 0
  152. }
  153. // IsWLocked checks whether the mutex is locked by writing lock.
  154. // Note that the result might be changed after it's called,
  155. // so it cannot be the criterion for atomic operations.
  156. func (m *Mutex) IsWLocked() bool {
  157. return m.state.Val() < 0
  158. }
  159. // IsRLocked checks whether the mutex is locked by reading lock.
  160. // Note that the result might be changed after it's called,
  161. // so it cannot be the criterion for atomic operations.
  162. func (m *Mutex) IsRLocked() bool {
  163. return m.state.Val() > 0
  164. }
  165. // LockFunc locks the mutex for writing with given callback function <f>.
  166. // If there's a write/reading lock the mutex, it will blocks until the lock is released.
  167. //
  168. // It releases the lock after <f> is executed.
  169. func (m *Mutex) LockFunc(f func()) {
  170. m.Lock()
  171. defer m.Unlock()
  172. f()
  173. }
  174. // RLockFunc locks the mutex for reading with given callback function <f>.
  175. // If there's a writing lock the mutex, it will blocks until the lock is released.
  176. //
  177. // It releases the lock after <f> is executed.
  178. func (m *Mutex) RLockFunc(f func()) {
  179. m.RLock()
  180. defer m.RUnlock()
  181. f()
  182. }
  183. // TryLockFunc tries locking the mutex for writing with given callback function <f>.
  184. // it returns true immediately if success, or if there's a write/reading lock on the mutex,
  185. // it returns false immediately.
  186. //
  187. // It releases the lock after <f> is executed.
  188. func (m *Mutex) TryLockFunc(f func()) (result bool) {
  189. if m.TryLock() {
  190. result = true
  191. defer m.Unlock()
  192. f()
  193. }
  194. return
  195. }
  196. // TryRLockFunc tries locking the mutex for reading with given callback function <f>.
  197. // It returns true immediately if success, or if there's a writing lock on the mutex,
  198. // it returns false immediately.
  199. //
  200. // It releases the lock after <f> is executed.
  201. func (m *Mutex) TryRLockFunc(f func()) (result bool) {
  202. if m.TryRLock() {
  203. result = true
  204. defer m.RUnlock()
  205. f()
  206. }
  207. return
  208. }