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