Skip to content

Commit

Permalink
sync: add Mutex.TryLock method
Browse files Browse the repository at this point in the history
Adds a way to lock a Mutex optimistically, without blocking.

```
var mu Mutex
if mu.TryLock() {
	// lock attempt succeeded
	mu.Unlock()
} else {
	// attempt failed - do something else
}
```

Refs: golang#45435
Refs: golang#6123
  • Loading branch information
puzpuzpuz committed Apr 27, 2021
1 parent cb34026 commit 03fd190
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/sync/mutex.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ const (
starvationThresholdNs = 1e6
)

// TryLock either locks m immediately if it is not in use, returning
// true, or, when a lock on m is already held, immediately returns false.
func (m *Mutex) TryLock() bool {
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return true
}
return false
}

// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
Expand Down
55 changes: 55 additions & 0 deletions src/sync/mutex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,27 @@ func TestMutex(t *testing.T) {
}
}

func SpinMutex(m *Mutex, cdone chan bool) {
for {
if m.TryLock() {
m.Unlock()
cdone <- true
return
}
}
}

func TestMutexSpin(t *testing.T) {
m := new(Mutex)
c := make(chan bool)
for i := 0; i < 100; i++ {
go SpinMutex(m, c)
}
for i := 0; i < 100; i++ {
<-c
}
}

var misuseTests = []struct {
name string
f func()
Expand All @@ -101,6 +122,15 @@ var misuseTests = []struct {
mu.Unlock()
},
},
{
"Mutex.Unlock3",
func() {
var mu Mutex
mu.TryLock()
mu.Unlock()
mu.Unlock()
},
},
{
"RWMutex.Unlock",
func() {
Expand Down Expand Up @@ -210,6 +240,31 @@ func TestMutexFairness(t *testing.T) {
}
}

func TestMutexTryLock(t *testing.T) {
var mu Mutex
if !mu.TryLock() {
t.Fatalf("initial TryLock failed unexpectedly")
}
if mu.TryLock() {
t.Fatalf("TryLock on locked Mutex succeeded unexpectedly")
}
mu.Unlock()
if !mu.TryLock() {
t.Fatalf("TryLock on unlocked Mutex failed unexpectedly")
}
done := make(chan bool, 1)
go func() {
mu.Lock()
done <- true
}()
select {
case <-done:
t.Fatalf("Lock on Mutex locked with TryLock succeeded unexpectedly")
case <-time.After(100 * time.Microsecond):
mu.Unlock()
}
}

func BenchmarkMutexUncontended(b *testing.B) {
type PaddedMutex struct {
Mutex
Expand Down

0 comments on commit 03fd190

Please sign in to comment.