Skip to content

Commit 2c036dc

Browse files
committed
sync: implement futex-based Mutex
1 parent 18443c1 commit 2c036dc

File tree

1 file changed

+20
-19
lines changed

1 file changed

+20
-19
lines changed

src/sync/mutex.go

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,34 @@ import (
66
)
77

88
type Mutex struct {
9-
locked bool
10-
blocked task.Stack
9+
futex task.Futex
1110
}
1211

1312
//go:linkname scheduleTask runtime.scheduleTask
1413
func scheduleTask(*task.Task)
1514

1615
func (m *Mutex) Lock() {
17-
if m.locked {
18-
// Push self onto stack of blocked tasks, and wait to be resumed.
19-
m.blocked.Push(task.Current())
20-
task.Pause()
16+
// Fast path: try to take an uncontended lock.
17+
if m.futex.CompareAndSwap(0, 1) {
18+
// We obtained the mutex.
2119
return
2220
}
2321

24-
m.locked = true
22+
// Try to lock the mutex. If it changed from 0 to 2, we took a contended
23+
// lock.
24+
for m.futex.Swap(2) != 0 {
25+
// Wait until we get resumed in Unlock.
26+
m.futex.Wait(2)
27+
}
2528
}
2629

2730
func (m *Mutex) Unlock() {
28-
if !m.locked {
31+
if old := m.futex.Swap(0); old == 0 {
32+
// Mutex wasn't locked before.
2933
panic("sync: unlock of unlocked Mutex")
30-
}
31-
32-
// Wake up a blocked task, if applicable.
33-
if t := m.blocked.Pop(); t != nil {
34-
scheduleTask(t)
35-
} else {
36-
m.locked = false
34+
} else if old == 2 {
35+
// Mutex was a contended lock, so we need to wake the next waiter.
36+
m.futex.Wake()
3737
}
3838
}
3939

@@ -43,11 +43,12 @@ func (m *Mutex) Unlock() {
4343
// and use of TryLock is often a sign of a deeper problem
4444
// in a particular use of mutexes.
4545
func (m *Mutex) TryLock() bool {
46-
if m.locked {
47-
return false
46+
// Fast path: try to take an uncontended lock.
47+
if m.futex.CompareAndSwap(0, 1) {
48+
// We obtained the mutex.
49+
return true
4850
}
49-
m.Lock()
50-
return true
51+
return false
5152
}
5253

5354
type RWMutex struct {

0 commit comments

Comments
 (0)