2021-01-17 21:46:25 +08:00
|
|
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
2018-08-30 00:00:15 +08:00
|
|
|
//
|
|
|
|
// 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,
|
2019-02-02 16:18:25 +08:00
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-08-30 00:00:15 +08:00
|
|
|
|
2019-06-21 22:23:07 +08:00
|
|
|
// Package gmutex implements graceful concurrent-safe mutex with more rich features.
|
|
|
|
package gmutex
|
2018-08-30 00:00:15 +08:00
|
|
|
|
|
|
|
import (
|
2019-06-21 22:23:07 +08:00
|
|
|
"math"
|
2019-06-18 17:31:48 +08:00
|
|
|
"runtime"
|
2019-06-21 22:23:07 +08:00
|
|
|
|
2021-10-11 21:41:56 +08:00
|
|
|
"github.com/gogf/gf/v2/container/gtype"
|
2018-08-30 00:00:15 +08:00
|
|
|
)
|
|
|
|
|
2021-12-30 00:20:38 +08:00
|
|
|
// Mutex is a high level Mutex, which implements more rich features for mutex.
|
2018-08-30 00:00:15 +08:00
|
|
|
type Mutex struct {
|
2020-12-15 13:03:12 +08:00
|
|
|
state *gtype.Int32 // Indicates the state of mutex. -1: writing locked; > 1 reading locked.
|
2019-06-21 22:37:07 +08:00
|
|
|
writer *gtype.Int32 // Pending writer count.
|
2019-06-21 22:23:07 +08:00
|
|
|
reader *gtype.Int32 // Pending reader count.
|
2019-07-13 16:56:20 +08:00
|
|
|
writing chan struct{} // Channel for writer blocking.
|
|
|
|
reading chan struct{} // Channel for reader blocking.
|
2018-08-30 00:00:15 +08:00
|
|
|
}
|
|
|
|
|
2019-06-21 22:23:07 +08:00
|
|
|
// New creates and returns a new mutex.
|
|
|
|
func New() *Mutex {
|
2019-06-19 09:06:52 +08:00
|
|
|
return &Mutex{
|
|
|
|
state: gtype.NewInt32(),
|
2019-06-21 22:23:07 +08:00
|
|
|
writer: gtype.NewInt32(),
|
|
|
|
reader: gtype.NewInt32(),
|
|
|
|
writing: make(chan struct{}, 1),
|
|
|
|
reading: make(chan struct{}, math.MaxInt32),
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2018-08-30 00:00:15 +08:00
|
|
|
}
|
|
|
|
|
2019-07-16 10:25:10 +08:00
|
|
|
// Lock locks the mutex for writing purpose.
|
|
|
|
// If the mutex is already locked by another goroutine for reading or writing,
|
2019-07-13 16:56:20 +08:00
|
|
|
// it blocks until the lock is available.
|
2019-06-05 23:50:24 +08:00
|
|
|
func (m *Mutex) Lock() {
|
2019-06-21 22:23:07 +08:00
|
|
|
for {
|
2019-07-13 16:56:20 +08:00
|
|
|
// Using CAS operation to get the writing lock atomically.
|
2019-07-13 16:41:23 +08:00
|
|
|
if m.state.Cas(0, -1) {
|
2019-06-21 22:23:07 +08:00
|
|
|
return
|
|
|
|
}
|
2019-07-16 10:25:10 +08:00
|
|
|
// It or else blocks to wait for the next chance.
|
2019-06-21 22:23:07 +08:00
|
|
|
m.writer.Add(1)
|
|
|
|
<-m.writing
|
2019-06-18 17:31:48 +08:00
|
|
|
}
|
2018-08-30 00:00:15 +08:00
|
|
|
}
|
|
|
|
|
2019-07-16 10:25:10 +08:00
|
|
|
// Unlock unlocks writing lock on the mutex.
|
2020-03-25 23:36:56 +08:00
|
|
|
// It is safe to be called multiple times even there's no locks.
|
2019-06-05 23:50:24 +08:00
|
|
|
func (m *Mutex) Unlock() {
|
2019-06-18 17:31:48 +08:00
|
|
|
if m.state.Cas(-1, 0) {
|
2019-07-16 10:25:10 +08:00
|
|
|
// Note that there might be more than one goroutines can enter this block.
|
|
|
|
var n int32
|
2019-06-21 22:23:07 +08:00
|
|
|
// Writing lock unlocks, then first check the blocked readers.
|
2020-12-15 13:03:12 +08:00
|
|
|
// If there are readers blocked, it unlocks them with preemption.
|
2019-07-16 10:25:10 +08:00
|
|
|
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
|
|
|
|
}
|
2019-06-21 22:23:07 +08:00
|
|
|
}
|
2019-07-16 10:25:10 +08:00
|
|
|
|
2019-07-13 16:56:20 +08:00
|
|
|
// It then also kindly feeds the pending writers with one chance.
|
2019-07-16 10:25:10 +08:00
|
|
|
if n = m.writer.Val(); n > 0 {
|
|
|
|
if m.writer.Cas(n, n-1) {
|
|
|
|
m.writing <- struct{}{}
|
|
|
|
}
|
2019-06-21 22:23:07 +08:00
|
|
|
}
|
2019-06-18 08:37:21 +08:00
|
|
|
}
|
2018-08-30 00:00:15 +08:00
|
|
|
}
|
|
|
|
|
2019-07-16 10:25:10 +08:00
|
|
|
// TryLock tries locking the mutex for writing purpose.
|
2019-07-13 16:56:20 +08:00
|
|
|
// It returns true immediately if success, or if there's a write/reading lock on the mutex,
|
|
|
|
// it returns false immediately.
|
2019-06-18 17:31:48 +08:00
|
|
|
func (m *Mutex) TryLock() bool {
|
2022-11-01 20:12:21 +08:00
|
|
|
return m.state.Cas(0, -1)
|
2019-06-18 17:31:48 +08:00
|
|
|
}
|
|
|
|
|
2020-03-25 23:36:56 +08:00
|
|
|
// RLock locks mutex for reading purpose.
|
2019-06-05 23:50:24 +08:00
|
|
|
// If the mutex is already locked for writing,
|
2019-07-13 16:56:20 +08:00
|
|
|
// it blocks until the lock is available.
|
2019-06-05 23:50:24 +08:00
|
|
|
func (m *Mutex) RLock() {
|
2019-06-21 22:37:07 +08:00
|
|
|
var n int32
|
2019-06-21 22:23:07 +08:00
|
|
|
for {
|
2019-07-13 16:41:23 +08:00
|
|
|
if n = m.state.Val(); n >= 0 {
|
2019-07-16 10:25:10 +08:00
|
|
|
// If there's no writing lock currently, then do the reading lock checks.
|
2019-06-21 22:23:07 +08:00
|
|
|
if m.state.Cas(n, n+1) {
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
runtime.Gosched()
|
|
|
|
}
|
2019-07-16 10:25:10 +08:00
|
|
|
} else {
|
|
|
|
// It or else pends the reader.
|
|
|
|
m.reader.Add(1)
|
|
|
|
<-m.reading
|
2019-06-21 22:23:07 +08:00
|
|
|
}
|
2019-06-18 17:31:48 +08:00
|
|
|
}
|
2018-08-30 00:00:15 +08:00
|
|
|
}
|
|
|
|
|
2019-07-16 10:25:10 +08:00
|
|
|
// RUnlock unlocks the reading lock on the mutex.
|
2020-03-25 23:36:56 +08:00
|
|
|
// It is safe to be called multiple times even there's no locks.
|
2019-06-05 23:50:24 +08:00
|
|
|
func (m *Mutex) RUnlock() {
|
2019-06-21 22:23:07 +08:00
|
|
|
var n int32
|
|
|
|
for {
|
|
|
|
if n = m.state.Val(); n >= 1 {
|
|
|
|
if m.state.Cas(n, n-1) {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
runtime.Gosched()
|
|
|
|
}
|
2019-06-18 17:31:48 +08:00
|
|
|
} else {
|
2019-06-21 22:23:07 +08:00
|
|
|
break
|
2019-06-18 17:31:48 +08:00
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2019-07-16 10:25:10 +08:00
|
|
|
// Reading lock unlocks, it then only check the blocked writers.
|
2019-07-13 16:56:20 +08:00
|
|
|
// Note that it is not necessary to check the pending readers here.
|
2021-10-21 18:22:47 +08:00
|
|
|
// `n == 1` means the state of mutex comes down to zero.
|
2019-07-16 10:25:10 +08:00
|
|
|
if n == 1 {
|
|
|
|
if n = m.writer.Val(); n > 0 {
|
|
|
|
if m.writer.Cas(n, n-1) {
|
|
|
|
m.writing <- struct{}{}
|
|
|
|
}
|
|
|
|
}
|
2019-06-21 22:23:07 +08:00
|
|
|
}
|
2018-08-30 00:00:15 +08:00
|
|
|
}
|
2018-12-30 14:53:16 +08:00
|
|
|
|
2019-07-16 10:25:10 +08:00
|
|
|
// TryRLock tries locking the mutex for reading purpose.
|
2019-07-13 16:56:20 +08:00
|
|
|
// It returns true immediately if success, or if there's a writing lock on the mutex,
|
|
|
|
// it returns false immediately.
|
2019-06-05 23:50:24 +08:00
|
|
|
func (m *Mutex) TryRLock() bool {
|
2019-06-21 22:37:07 +08:00
|
|
|
var n int32
|
2019-06-21 22:23:07 +08:00
|
|
|
for {
|
2019-07-13 16:41:23 +08:00
|
|
|
if n = m.state.Val(); n >= 0 {
|
2019-06-21 22:23:07 +08:00
|
|
|
if m.state.Cas(n, n+1) {
|
|
|
|
return true
|
|
|
|
} else {
|
|
|
|
runtime.Gosched()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return false
|
2019-06-18 17:31:48 +08:00
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2018-12-30 14:53:16 +08:00
|
|
|
}
|
|
|
|
|
2019-06-21 22:23:07 +08:00
|
|
|
// 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.
|
2019-06-18 17:39:28 +08:00
|
|
|
func (m *Mutex) IsLocked() bool {
|
|
|
|
return m.state.Val() != 0
|
|
|
|
}
|
|
|
|
|
2019-06-21 22:23:07 +08:00
|
|
|
// 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.
|
2019-06-18 17:39:28 +08:00
|
|
|
func (m *Mutex) IsWLocked() bool {
|
|
|
|
return m.state.Val() < 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsRLocked checks whether the mutex is locked by reading lock.
|
2019-06-21 22:23:07 +08:00
|
|
|
// Note that the result might be changed after it's called,
|
|
|
|
// so it cannot be the criterion for atomic operations.
|
2019-06-18 17:39:28 +08:00
|
|
|
func (m *Mutex) IsRLocked() bool {
|
|
|
|
return m.state.Val() > 0
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:22:47 +08:00
|
|
|
// LockFunc locks the mutex for writing with given callback function `f`.
|
2019-06-22 14:42:27 +08:00
|
|
|
// If there's a write/reading lock the mutex, it will blocks until the lock is released.
|
|
|
|
//
|
2021-10-21 18:22:47 +08:00
|
|
|
// It releases the lock after `f` is executed.
|
2019-06-22 14:42:27 +08:00
|
|
|
func (m *Mutex) LockFunc(f func()) {
|
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
|
|
|
f()
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:22:47 +08:00
|
|
|
// RLockFunc locks the mutex for reading with given callback function `f`.
|
2019-06-22 14:42:27 +08:00
|
|
|
// If there's a writing lock the mutex, it will blocks until the lock is released.
|
|
|
|
//
|
2021-10-21 18:22:47 +08:00
|
|
|
// It releases the lock after `f` is executed.
|
2019-06-22 14:42:27 +08:00
|
|
|
func (m *Mutex) RLockFunc(f func()) {
|
|
|
|
m.RLock()
|
|
|
|
defer m.RUnlock()
|
|
|
|
f()
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:22:47 +08:00
|
|
|
// TryLockFunc tries locking the mutex for writing with given callback function `f`.
|
2019-07-13 16:56:20 +08:00
|
|
|
// it returns true immediately if success, or if there's a write/reading lock on the mutex,
|
|
|
|
// it returns false immediately.
|
2019-06-05 23:50:24 +08:00
|
|
|
//
|
2021-10-21 18:22:47 +08:00
|
|
|
// It releases the lock after `f` is executed.
|
2019-07-13 16:56:20 +08:00
|
|
|
func (m *Mutex) TryLockFunc(f func()) (result bool) {
|
2019-06-05 23:50:24 +08:00
|
|
|
if m.TryLock() {
|
2019-07-13 16:56:20 +08:00
|
|
|
result = true
|
2019-06-05 23:50:24 +08:00
|
|
|
defer m.Unlock()
|
|
|
|
f()
|
|
|
|
}
|
2019-07-13 16:56:20 +08:00
|
|
|
return
|
2019-06-05 23:50:24 +08:00
|
|
|
}
|
|
|
|
|
2021-10-21 18:22:47 +08:00
|
|
|
// TryRLockFunc tries locking the mutex for reading with given callback function `f`.
|
2019-07-13 16:56:20 +08:00
|
|
|
// It returns true immediately if success, or if there's a writing lock on the mutex,
|
|
|
|
// it returns false immediately.
|
2019-06-05 23:50:24 +08:00
|
|
|
//
|
2021-10-21 18:22:47 +08:00
|
|
|
// It releases the lock after `f` is executed.
|
2019-07-13 16:56:20 +08:00
|
|
|
func (m *Mutex) TryRLockFunc(f func()) (result bool) {
|
2019-06-05 23:50:24 +08:00
|
|
|
if m.TryRLock() {
|
2019-07-13 16:56:20 +08:00
|
|
|
result = true
|
2019-06-05 23:50:24 +08:00
|
|
|
defer m.RUnlock()
|
|
|
|
f()
|
|
|
|
}
|
2019-07-13 16:56:20 +08:00
|
|
|
return
|
2019-06-05 23:50:24 +08:00
|
|
|
}
|