11package sync
22
33import (
4+ "internal/llsync"
45 "internal/task"
56 _ "unsafe"
67)
78
89type Mutex struct {
9- locked bool
10- blocked task.Stack
10+ futex llsync.Futex
1111}
1212
1313//go:linkname resumeTask runtime.resumeTask
1414func resumeTask (* task.Task )
1515
1616func (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 ()
17+ // Fast path: try to take an uncontended lock.
18+ if m .futex .CompareAndSwap (0 , 1 ) {
19+ // We obtained the mutex.
2120 return
2221 }
2322
24- m .locked = true
23+ // Try to lock the mutex. If it changed from 0 to 2, we took a contended
24+ // lock.
25+ for m .futex .Swap (2 ) != 0 {
26+ // Wait until we get resumed in Unlock.
27+ m .futex .Wait (2 )
28+ }
2529}
2630
2731func (m * Mutex ) Unlock () {
28- if ! m .locked {
32+ if old := m .futex .Swap (0 ); old == 0 {
33+ // Mutex wasn't locked before.
2934 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- resumeTask (t )
35- } else {
36- m .locked = false
35+ } else if old == 2 {
36+ // Mutex was a contended lock, so we need to wake the next waiter.
37+ m .futex .Wake ()
3738 }
3839}
3940
@@ -43,11 +44,12 @@ func (m *Mutex) Unlock() {
4344// and use of TryLock is often a sign of a deeper problem
4445// in a particular use of mutexes.
4546func (m * Mutex ) TryLock () bool {
46- if m .locked {
47- return false
47+ // Fast path: try to take an uncontended lock.
48+ if m .futex .CompareAndSwap (0 , 1 ) {
49+ // We obtained the mutex.
50+ return true
4851 }
49- m .Lock ()
50- return true
52+ return false
5153}
5254
5355type RWMutex struct {
0 commit comments