123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
- //
- // This Source Code Form is subject to the terms of the MIT License.
- // If a copy of the MIT was not distributed with this file,
- // You can obtain one at https://github.com/gogf/gf.
- // Package gmutex implements graceful concurrent-safe mutex with more rich features.
- package gmutex
- import (
- "math"
- "runtime"
- "github.com/gogf/gf/container/gtype"
- )
- // The high level Mutex, which implements more rich features for mutex.
- type Mutex struct {
- state *gtype.Int32 // Indicates the state of mutex. -1: writing locked; > 1 reading locked.
- writer *gtype.Int32 // Pending writer count.
- reader *gtype.Int32 // Pending reader count.
- writing chan struct{} // Channel for writer blocking.
- reading chan struct{} // Channel for reader blocking.
- }
- // New creates and returns a new mutex.
- func New() *Mutex {
- return &Mutex{
- state: gtype.NewInt32(),
- writer: gtype.NewInt32(),
- reader: gtype.NewInt32(),
- writing: make(chan struct{}, 1),
- reading: make(chan struct{}, math.MaxInt32),
- }
- }
- // Lock locks the mutex for writing purpose.
- // If the mutex is already locked by another goroutine for reading or writing,
- // it blocks until the lock is available.
- func (m *Mutex) Lock() {
- for {
- // Using CAS operation to get the writing lock atomically.
- if m.state.Cas(0, -1) {
- return
- }
- // It or else blocks to wait for the next chance.
- m.writer.Add(1)
- <-m.writing
- }
- }
- // Unlock unlocks writing lock on the mutex.
- // It is safe to be called multiple times even there's no locks.
- func (m *Mutex) Unlock() {
- if m.state.Cas(-1, 0) {
- // Note that there might be more than one goroutines can enter this block.
- var n int32
- // Writing lock unlocks, then first check the blocked readers.
- // If there are readers blocked, it unlocks them with preemption.
- for {
- if n = m.reader.Val(); n > 0 {
- if m.reader.Cas(n, 0) {
- for ; n > 0; n-- {
- m.reading <- struct{}{}
- }
- break
- } else {
- runtime.Gosched()
- }
- } else {
- break
- }
- }
- // It then also kindly feeds the pending writers with one chance.
- if n = m.writer.Val(); n > 0 {
- if m.writer.Cas(n, n-1) {
- m.writing <- struct{}{}
- }
- }
- }
- }
- // TryLock tries locking the mutex for writing purpose.
- // It returns true immediately if success, or if there's a write/reading lock on the mutex,
- // it returns false immediately.
- func (m *Mutex) TryLock() bool {
- if m.state.Cas(0, -1) {
- return true
- }
- return false
- }
- // RLock locks mutex for reading purpose.
- // If the mutex is already locked for writing,
- // it blocks until the lock is available.
- func (m *Mutex) RLock() {
- var n int32
- for {
- if n = m.state.Val(); n >= 0 {
- // If there's no writing lock currently, then do the reading lock checks.
- if m.state.Cas(n, n+1) {
- return
- } else {
- runtime.Gosched()
- }
- } else {
- // It or else pends the reader.
- m.reader.Add(1)
- <-m.reading
- }
- }
- }
- // RUnlock unlocks the reading lock on the mutex.
- // It is safe to be called multiple times even there's no locks.
- func (m *Mutex) RUnlock() {
- var n int32
- for {
- if n = m.state.Val(); n >= 1 {
- if m.state.Cas(n, n-1) {
- break
- } else {
- runtime.Gosched()
- }
- } else {
- break
- }
- }
- // Reading lock unlocks, it then only check the blocked writers.
- // Note that it is not necessary to check the pending readers here.
- // <n == 1> means the state of mutex comes down to zero.
- if n == 1 {
- if n = m.writer.Val(); n > 0 {
- if m.writer.Cas(n, n-1) {
- m.writing <- struct{}{}
- }
- }
- }
- }
- // TryRLock tries locking the mutex for reading purpose.
- // It returns true immediately if success, or if there's a writing lock on the mutex,
- // it returns false immediately.
- func (m *Mutex) TryRLock() bool {
- var n int32
- for {
- if n = m.state.Val(); n >= 0 {
- if m.state.Cas(n, n+1) {
- return true
- } else {
- runtime.Gosched()
- }
- } else {
- return false
- }
- }
- }
- // IsLocked checks whether the mutex is locked with writing or reading lock.
- // Note that the result might be changed after it's called,
- // so it cannot be the criterion for atomic operations.
- func (m *Mutex) IsLocked() bool {
- return m.state.Val() != 0
- }
- // IsWLocked checks whether the mutex is locked by writing lock.
- // Note that the result might be changed after it's called,
- // so it cannot be the criterion for atomic operations.
- func (m *Mutex) IsWLocked() bool {
- return m.state.Val() < 0
- }
- // IsRLocked checks whether the mutex is locked by reading lock.
- // Note that the result might be changed after it's called,
- // so it cannot be the criterion for atomic operations.
- func (m *Mutex) IsRLocked() bool {
- return m.state.Val() > 0
- }
- // LockFunc locks the mutex for writing with given callback function <f>.
- // If there's a write/reading lock the mutex, it will blocks until the lock is released.
- //
- // It releases the lock after <f> is executed.
- func (m *Mutex) LockFunc(f func()) {
- m.Lock()
- defer m.Unlock()
- f()
- }
- // RLockFunc locks the mutex for reading with given callback function <f>.
- // If there's a writing lock the mutex, it will blocks until the lock is released.
- //
- // It releases the lock after <f> is executed.
- func (m *Mutex) RLockFunc(f func()) {
- m.RLock()
- defer m.RUnlock()
- f()
- }
- // TryLockFunc tries locking the mutex for writing with given callback function <f>.
- // it returns true immediately if success, or if there's a write/reading lock on the mutex,
- // it returns false immediately.
- //
- // It releases the lock after <f> is executed.
- func (m *Mutex) TryLockFunc(f func()) (result bool) {
- if m.TryLock() {
- result = true
- defer m.Unlock()
- f()
- }
- return
- }
- // TryRLockFunc tries locking the mutex for reading with given callback function <f>.
- // It returns true immediately if success, or if there's a writing lock on the mutex,
- // it returns false immediately.
- //
- // It releases the lock after <f> is executed.
- func (m *Mutex) TryRLockFunc(f func()) (result bool) {
- if m.TryRLock() {
- result = true
- defer m.RUnlock()
- f()
- }
- return
- }
|