diff --git a/os/gmlock/gmlock_locker.go b/os/gmlock/gmlock_locker.go index 5336f92db..b802d76f8 100644 --- a/os/gmlock/gmlock_locker.go +++ b/os/gmlock/gmlock_locker.go @@ -7,8 +7,9 @@ package gmlock import ( + "sync" + "github.com/gogf/gf/v2/container/gmap" - "github.com/gogf/gf/v2/os/gmutex" ) // Locker is a memory based locker. @@ -42,7 +43,7 @@ func (l *Locker) TryLock(key string) bool { // Unlock unlocks the writing lock of the `key`. func (l *Locker) Unlock(key string) { if v := l.m.Get(key); v != nil { - v.(*gmutex.Mutex).Unlock() + v.(*sync.RWMutex).Unlock() } } @@ -62,7 +63,7 @@ func (l *Locker) TryRLock(key string) bool { // RUnlock unlocks the reading lock of the `key`. func (l *Locker) RUnlock(key string) { if v := l.m.Get(key); v != nil { - v.(*gmutex.Mutex).RUnlock() + v.(*sync.RWMutex).RUnlock() } } @@ -126,8 +127,8 @@ func (l *Locker) Clear() { // getOrNewMutex returns the mutex of given `key` if it exists, // or else creates and returns a new one. -func (l *Locker) getOrNewMutex(key string) *gmutex.Mutex { +func (l *Locker) getOrNewMutex(key string) *sync.RWMutex { return l.m.GetOrSetFuncLock(key, func() interface{} { - return gmutex.New() - }).(*gmutex.Mutex) + return &sync.RWMutex{} + }).(*sync.RWMutex) } diff --git a/os/gmutex/gmutex.go b/os/gmutex/gmutex.go deleted file mode 100644 index df5e40c49..000000000 --- a/os/gmutex/gmutex.go +++ /dev/null @@ -1,224 +0,0 @@ -// 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/v2/container/gtype" -) - -// Mutex is a 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 { - return m.state.Cas(0, -1) -} - -// 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 -} diff --git a/os/gmutex/gmutex_z_bench_test.go b/os/gmutex/gmutex_z_bench_test.go deleted file mode 100644 index 9c8380ddf..000000000 --- a/os/gmutex/gmutex_z_bench_test.go +++ /dev/null @@ -1,85 +0,0 @@ -// 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_test - -import ( - "sync" - "testing" - - "github.com/gogf/gf/v2/os/gmutex" -) - -var ( - mu = sync.Mutex{} - rwmu = sync.RWMutex{} - gmu = gmutex.New() -) - -func Benchmark_Mutex_LockUnlock(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - mu.Lock() - mu.Unlock() - } - }) -} - -func Benchmark_RWMutex_LockUnlock(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - rwmu.Lock() - rwmu.Unlock() - } - }) -} - -func Benchmark_RWMutex_RLockRUnlock(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - rwmu.RLock() - rwmu.RUnlock() - } - }) -} - -func Benchmark_GMutex_LockUnlock(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - gmu.Lock() - gmu.Unlock() - } - }) -} - -func Benchmark_GMutex_TryLock(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - if gmu.TryLock() { - gmu.Unlock() - } - } - }) -} - -func Benchmark_GMutex_RLockRUnlock(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - gmu.RLock() - gmu.RUnlock() - } - }) -} - -func Benchmark_GMutex_TryRLock(b *testing.B) { - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - if gmu.TryRLock() { - gmu.RUnlock() - } - } - }) -} diff --git a/os/gmutex/gmutex_z_unit_test.go b/os/gmutex/gmutex_z_unit_test.go deleted file mode 100644 index 993dd04ff..000000000 --- a/os/gmutex/gmutex_z_unit_test.go +++ /dev/null @@ -1,280 +0,0 @@ -// 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_test - -import ( - "context" - "testing" - "time" - - "github.com/gogf/gf/v2/container/garray" - "github.com/gogf/gf/v2/os/glog" - "github.com/gogf/gf/v2/os/gmutex" - "github.com/gogf/gf/v2/test/gtest" -) - -func Test_Mutex_RUnlock(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - mu := gmutex.New() - for index := 0; index < 1000; index++ { - go func() { - mu.RLockFunc(func() { - time.Sleep(200 * time.Millisecond) - }) - }() - } - time.Sleep(100 * time.Millisecond) - t.Assert(mu.IsRLocked(), true) - t.Assert(mu.IsLocked(), true) - t.Assert(mu.IsWLocked(), false) - for index := 0; index < 1000; index++ { - go func() { - mu.RUnlock() - }() - } - time.Sleep(300 * time.Millisecond) - t.Assert(mu.IsRLocked(), false) - - }) - - // RLock before Lock - gtest.C(t, func(t *gtest.T) { - mu := gmutex.New() - mu.RLock() - go func() { - mu.Lock() - time.Sleep(300 * time.Millisecond) - mu.Unlock() - }() - time.Sleep(100 * time.Millisecond) - mu.RUnlock() - t.Assert(mu.IsRLocked(), false) - time.Sleep(100 * time.Millisecond) - t.Assert(mu.IsLocked(), true) - time.Sleep(400 * time.Millisecond) - t.Assert(mu.IsLocked(), false) - }) -} - -func Test_Mutex_IsLocked(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - mu := gmutex.New() - go func() { - mu.LockFunc(func() { - time.Sleep(200 * time.Millisecond) - }) - }() - time.Sleep(100 * time.Millisecond) - t.Assert(mu.IsLocked(), true) - t.Assert(mu.IsWLocked(), true) - t.Assert(mu.IsRLocked(), false) - time.Sleep(300 * time.Millisecond) - t.Assert(mu.IsLocked(), false) - t.Assert(mu.IsWLocked(), false) - - go func() { - mu.RLockFunc(func() { - time.Sleep(200 * time.Millisecond) - }) - }() - time.Sleep(100 * time.Millisecond) - t.Assert(mu.IsRLocked(), true) - t.Assert(mu.IsLocked(), true) - t.Assert(mu.IsWLocked(), false) - time.Sleep(300 * time.Millisecond) - t.Assert(mu.IsRLocked(), false) - }) -} - -func Test_Mutex_Unlock(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - mu := gmutex.New() - array := garray.New(true) - go func() { - mu.LockFunc(func() { - array.Append(1) - time.Sleep(300 * time.Millisecond) - }) - }() - go func() { - time.Sleep(100 * time.Millisecond) - mu.LockFunc(func() { - array.Append(1) - }) - }() - go func() { - time.Sleep(100 * time.Millisecond) - mu.LockFunc(func() { - array.Append(1) - }) - }() - - go func() { - time.Sleep(200 * time.Millisecond) - mu.Unlock() - mu.Unlock() - mu.Unlock() - mu.Unlock() - }() - - time.Sleep(100 * time.Millisecond) - t.Assert(array.Len(), 1) - time.Sleep(400 * time.Millisecond) - t.Assert(array.Len(), 3) - }) -} - -func Test_Mutex_LockFunc(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - mu := gmutex.New() - array := garray.New(true) - go func() { - mu.LockFunc(func() { - array.Append(1) - time.Sleep(300 * time.Millisecond) - }) - }() - go func() { - time.Sleep(100 * time.Millisecond) - mu.LockFunc(func() { - array.Append(1) - }) - }() - time.Sleep(100 * time.Millisecond) - t.Assert(array.Len(), 1) - time.Sleep(100 * time.Millisecond) - t.Assert(array.Len(), 1) - time.Sleep(200 * time.Millisecond) - t.Assert(array.Len(), 2) - }) -} - -func Test_Mutex_TryLockFunc(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - mu := gmutex.New() - array := garray.New(true) - go func() { - mu.LockFunc(func() { - array.Append(1) - time.Sleep(300 * time.Millisecond) - }) - }() - go func() { - time.Sleep(100 * time.Millisecond) - mu.TryLockFunc(func() { - array.Append(1) - }) - }() - go func() { - time.Sleep(400 * time.Millisecond) - mu.TryLockFunc(func() { - array.Append(1) - }) - }() - time.Sleep(100 * time.Millisecond) - t.Assert(array.Len(), 1) - time.Sleep(100 * time.Millisecond) - t.Assert(array.Len(), 1) - time.Sleep(300 * time.Millisecond) - t.Assert(array.Len(), 2) - }) -} - -func Test_Mutex_RLockFunc(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - mu := gmutex.New() - array := garray.New(true) - go func() { - mu.LockFunc(func() { - array.Append(1) - time.Sleep(300 * time.Millisecond) - }) - }() - go func() { - time.Sleep(100 * time.Millisecond) - mu.RLockFunc(func() { - array.Append(1) - time.Sleep(100 * time.Millisecond) - }) - }() - time.Sleep(100 * time.Millisecond) - t.Assert(array.Len(), 1) - time.Sleep(100 * time.Millisecond) - t.Assert(array.Len(), 1) - time.Sleep(300 * time.Millisecond) - t.Assert(array.Len(), 2) - }) - - gtest.C(t, func(t *gtest.T) { - mu := gmutex.New() - array := garray.New(true) - go func() { - time.Sleep(100 * time.Millisecond) - mu.RLockFunc(func() { - array.Append(1) - time.Sleep(100 * time.Millisecond) - }) - }() - go func() { - time.Sleep(100 * time.Millisecond) - mu.RLockFunc(func() { - array.Append(1) - time.Sleep(100 * time.Millisecond) - }) - }() - go func() { - time.Sleep(100 * time.Millisecond) - mu.RLockFunc(func() { - array.Append(1) - time.Sleep(100 * time.Millisecond) - }) - }() - t.Assert(array.Len(), 0) - time.Sleep(200 * time.Millisecond) - t.Assert(array.Len(), 3) - }) -} - -func Test_Mutex_TryRLockFunc(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - var ( - mu = gmutex.New() - array = garray.New(true) - ) - // First writing lock - go func() { - mu.LockFunc(func() { - array.Append(1) - glog.Print(context.TODO(), "lock1 done") - time.Sleep(2000 * time.Millisecond) - }) - }() - // This goroutine never gets the lock. - go func() { - time.Sleep(1000 * time.Millisecond) - mu.TryRLockFunc(func() { - array.Append(1) - }) - }() - for index := 0; index < 1000; index++ { - go func() { - time.Sleep(4000 * time.Millisecond) - mu.TryRLockFunc(func() { - array.Append(1) - }) - }() - } - time.Sleep(1000 * time.Millisecond) - t.Assert(array.Len(), 1) - time.Sleep(1000 * time.Millisecond) - t.Assert(array.Len(), 1) - time.Sleep(1000 * time.Millisecond) - t.Assert(array.Len(), 1) - time.Sleep(2000 * time.Millisecond) - t.Assert(array.Len(), 1001) - }) -}