From 0b62ccf941b3c2b56b183084cfeb4fc78d05100e Mon Sep 17 00:00:00 2001 From: John Date: Tue, 16 Jul 2019 10:25:10 +0800 Subject: [PATCH] improve gmutex --- g/os/gmutex/gmutex.go | 66 ++++++++++++++++++++----------- g/os/gmutex/gmutex_bench_test.go | 68 +++++++++++++++++++------------- 2 files changed, 83 insertions(+), 51 deletions(-) diff --git a/g/os/gmutex/gmutex.go b/g/os/gmutex/gmutex.go index c232c3425..b59927f2c 100644 --- a/g/os/gmutex/gmutex.go +++ b/g/os/gmutex/gmutex.go @@ -34,8 +34,8 @@ func New() *Mutex { } } -// Lock locks the mutex for writing. -// If the lock is already locked for reading or writing, +// 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 { @@ -43,30 +43,45 @@ func (m *Mutex) Lock() { if m.state.Cas(0, -1) { return } - // Or else blocks to wait for the next chance. + // It or else blocks to wait for the next chance. m.writer.Add(1) <-m.writing - m.writer.Add(-1) } } -// Unlock unlocks the writing lock. +// Unlock unlocks writing lock on the mutex. // It is safe to be called multiple times if there's any locks or not. 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're readers blocked, unlock them with preemption. - for n := m.reader.Val(); n > 0; n-- { - m.reading <- struct{}{} + // If there're 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 m.writer.Val() > 0 { - m.writing <- struct{}{} + if n = m.writer.Val(); n > 0 { + if m.writer.Cas(n, n-1) { + m.writing <- struct{}{} + } } } } -// TryLock tries locking the mutex for writing. +// 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 { @@ -76,30 +91,28 @@ func (m *Mutex) TryLock() bool { return false } -// RLock locks mutex for reading purpose. +// RLock locks mutex for reading purpose 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 there're no writing lock currently, then do the reading lock checks. 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() - continue } + } else { + // It or else pends the reader. + m.reader.Add(1) + <-m.reading } - - // Or else pending the reader. - m.reader.Add(1) - <-m.reading - m.reader.Add(-1) } } -// RUnlock unlocks the reading lock. +// RUnlock unlocks the reading lock on the mutex. // It is safe to be called multiple times if there's any locks or not. func (m *Mutex) RUnlock() { var n int32 @@ -114,14 +127,19 @@ func (m *Mutex) RUnlock() { break } } - // Reading lock unlocks, then only check the blocked writers. + // Reading lock unlocks, it then only check the blocked writers. // Note that it is not necessary to check the pending readers here. - if n == 1 && m.writer.Val() > 0 { - m.writing <- struct{}{} + // 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. +// 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 { diff --git a/g/os/gmutex/gmutex_bench_test.go b/g/os/gmutex/gmutex_bench_test.go index 470f38953..2e67d89f2 100644 --- a/g/os/gmutex/gmutex_bench_test.go +++ b/g/os/gmutex/gmutex_bench_test.go @@ -20,50 +20,64 @@ var ( ) func Benchmark_Mutex_LockUnlock(b *testing.B) { - for i := 0; i < b.N; i++ { - mu.Lock() - mu.Unlock() - } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + mu.Lock() + mu.Unlock() + } + }) } func Benchmark_RWMutex_LockUnlock(b *testing.B) { - for i := 0; i < b.N; i++ { - rwmu.Lock() - rwmu.Unlock() - } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + rwmu.Lock() + rwmu.Unlock() + } + }) } func Benchmark_RWMutex_RLockRUnlock(b *testing.B) { - for i := 0; i < b.N; i++ { - rwmu.RLock() - rwmu.RUnlock() - } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + rwmu.RLock() + rwmu.RUnlock() + } + }) } func Benchmark_GMutex_LockUnlock(b *testing.B) { - for i := 0; i < b.N; i++ { - gmu.Lock() - gmu.Unlock() - } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + gmu.Lock() + gmu.Unlock() + } + }) } func Benchmark_GMutex_TryLock(b *testing.B) { - for i := 0; i < b.N; i++ { - if gmu.TryLock() { - defer gmu.Unlock() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if gmu.TryLock() { + defer gmu.Unlock() + } } - } + }) } func Benchmark_GMutex_RLockRUnlock(b *testing.B) { - for i := 0; i < b.N; i++ { - gmu.RLock() - gmu.RUnlock() - } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + gmu.RLock() + gmu.RUnlock() + } + }) } func Benchmark_GMutex_TryRLock(b *testing.B) { - for i := 0; i < b.N; i++ { - gmu.TryRLock() - } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + gmu.TryRLock() + } + }) }