]> Cypherpunks.ru repositories - gostls13.git/commitdiff
sync: add Mutex.TryLock, RWMutex.TryLock, RWMutex.TryRLock
authorRuss Cox <rsc@golang.org>
Thu, 13 May 2021 14:44:47 +0000 (10:44 -0400)
committerRuss Cox <rsc@golang.org>
Fri, 29 Oct 2021 17:13:13 +0000 (17:13 +0000)
Use of these functions is almost (but not) always a bad idea.

Very rarely they are necessary, and third-party implementations
(using a mutex and an atomic word, say) cannot integrate as well
with the race detector as implmentations in package sync itself.

Fixes #45435.

Change-Id: I0128ca48ef5e0a3b09c913f0f3a7ee5c56388000
Reviewed-on: https://go-review.googlesource.com/c/go/+/319769
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/sync/mutex.go
src/sync/mutex_test.go
src/sync/rwmutex.go
src/sync/rwmutex_test.go

index 3028552f743d1fce5e916244b1e141c83079ee0f..9dd04d947017f15d8a3663353e71c9322000a700 100644 (file)
@@ -81,6 +81,21 @@ func (m *Mutex) Lock() {
        m.lockSlow()
 }
 
+// TryLock tries to lock m and reports whether it succeeded.
+//
+// Note that while correct uses of TryLock do exist, they are rare,
+// and use of TryLock is often a sign of a deeper problem
+// in a particular use of mutexes.
+func (m *Mutex) TryLock() bool {
+       if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
+               if race.Enabled {
+                       race.Acquire(unsafe.Pointer(m))
+               }
+               return true
+       }
+       return false
+}
+
 func (m *Mutex) lockSlow() {
        var waitStartTime int64
        starving := false
index 98c1bf2a5f9bc2fb7386c7eafd5b58907ae0a56e..cca0986a30975ebd9c51cf37d97037b43b666dae 100644 (file)
@@ -60,6 +60,12 @@ func BenchmarkContendedSemaphore(b *testing.B) {
 
 func HammerMutex(m *Mutex, loops int, cdone chan bool) {
        for i := 0; i < loops; i++ {
+               if i%3 == 0 {
+                       if m.TryLock() {
+                               m.Unlock()
+                       }
+                       continue
+               }
                m.Lock()
                m.Unlock()
        }
@@ -71,7 +77,19 @@ func TestMutex(t *testing.T) {
                t.Logf("got mutexrate %d expected 0", n)
        }
        defer runtime.SetMutexProfileFraction(0)
+
        m := new(Mutex)
+
+       m.Lock()
+       if m.TryLock() {
+               t.Fatalf("TryLock succeeded with mutex locked")
+       }
+       m.Unlock()
+       if !m.TryLock() {
+               t.Fatalf("TryLock failed with mutex unlocked")
+       }
+       m.Unlock()
+
        c := make(chan bool)
        for i := 0; i < 10; i++ {
                go HammerMutex(m, 1000, c)
index 3012b5548e4147d8f6495d84363bb7161c2f9779..f0d4c9771a04907e9bb0c64586a9d49d71871b80 100644 (file)
@@ -68,6 +68,34 @@ func (rw *RWMutex) RLock() {
        }
 }
 
+// TryRLock tries to lock rw for reading and reports whether it succeeded.
+//
+// Note that while correct uses of TryRLock do exist, they are rare,
+// and use of TryRLock is often a sign of a deeper problem
+// in a particular use of mutexes.
+func (rw *RWMutex) TryRLock() bool {
+       if race.Enabled {
+               _ = rw.w.state
+               race.Disable()
+       }
+       for {
+               c := atomic.LoadInt32(&rw.readerCount)
+               if c < 0 {
+                       if race.Enabled {
+                               race.Enable()
+                       }
+                       return false
+               }
+               if atomic.CompareAndSwapInt32(&rw.readerCount, c, c+1) {
+                       if race.Enabled {
+                               race.Enable()
+                               race.Acquire(unsafe.Pointer(&rw.readerSem))
+                       }
+                       return true
+               }
+       }
+}
+
 // RUnlock undoes a single RLock call;
 // it does not affect other simultaneous readers.
 // It is a run-time error if rw is not locked for reading
@@ -122,6 +150,37 @@ func (rw *RWMutex) Lock() {
        }
 }
 
+// TryLock tries to lock rw for writing and reports whether it succeeded.
+//
+// Note that while correct uses of TryLock do exist, they are rare,
+// and use of TryLock is often a sign of a deeper problem
+// in a particular use of mutexes.
+func (rw *RWMutex) TryLock() bool {
+       if race.Enabled {
+               _ = rw.w.state
+               race.Disable()
+       }
+       if !rw.w.TryLock() {
+               if race.Enabled {
+                       race.Enable()
+               }
+               return false
+       }
+       if !atomic.CompareAndSwapInt32(&rw.readerCount, 0, -rwmutexMaxReaders) {
+               rw.w.Unlock()
+               if race.Enabled {
+                       race.Enable()
+               }
+               return false
+       }
+       if race.Enabled {
+               race.Enable()
+               race.Acquire(unsafe.Pointer(&rw.readerSem))
+               race.Acquire(unsafe.Pointer(&rw.writerSem))
+       }
+       return true
+}
+
 // Unlock unlocks rw for writing. It is a run-time error if rw is
 // not locked for writing on entry to Unlock.
 //
index c98e69fd07d7457b55a2fcfb9c5c875a8a8af05e..dfbdd9bbeef706afd38ef510019187f824f527ab 100644 (file)
@@ -108,6 +108,34 @@ func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) {
 }
 
 func TestRWMutex(t *testing.T) {
+       var m RWMutex
+
+       m.Lock()
+       if m.TryLock() {
+               t.Fatalf("TryLock succeeded with mutex locked")
+       }
+       if m.TryRLock() {
+               t.Fatalf("TryRLock succeeded with mutex locked")
+       }
+       m.Unlock()
+
+       if !m.TryLock() {
+               t.Fatalf("TryLock failed with mutex unlocked")
+       }
+       m.Unlock()
+
+       if !m.TryRLock() {
+               t.Fatalf("TryRLock failed with mutex unlocked")
+       }
+       if !m.TryRLock() {
+               t.Fatalf("TryRLock failed with mutex rlocked")
+       }
+       if m.TryLock() {
+               t.Fatalf("TryLock succeeded with mutex rlocked")
+       }
+       m.RUnlock()
+       m.RUnlock()
+
        defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
        n := 1000
        if testing.Short() {