Skip to content

Commit afbbc28

Browse files
rhyshgopherbot
authored andcommitted
Revert "runtime: double-link list of waiting Ms"
This reverts commit d881ed6 (CL 585637). Reason for revert: This is part of a patch series that changed the handling of contended lock2/unlock2 calls, reducing the maximum throughput of contended runtime.mutex values, and causing a performance regression on applications where that is (or became) the bottleneck. Updates #66999 Updates #67585 Change-Id: I70d8d0b74f73be95c43d664f584e8d98519aba26 Reviewed-on: https://go-review.googlesource.com/c/go/+/589116 Auto-Submit: Rhys Hiltner <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Than McIntosh <[email protected]> Reviewed-by: Michael Pratt <[email protected]>
1 parent 5dead59 commit afbbc28

File tree

3 files changed

+25
-324
lines changed

3 files changed

+25
-324
lines changed

src/runtime/lock_futex.go

Lines changed: 7 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,6 @@ const (
3737
// independently: a thread can enter lock2, observe that another thread is
3838
// already asleep, and immediately try to grab the lock anyway without waiting
3939
// for its "fair" turn.
40-
//
41-
// The rest of mutex.key holds a pointer to the head of a linked list of the Ms
42-
// that are waiting for the mutex. The pointer portion is set if and only if the
43-
// mutex_sleeping flag is set. Because the futex syscall operates on 32 bits but
44-
// a uintptr may be larger, the flag lets us be sure the futexsleep call will
45-
// only commit if the pointer portion is unset. Otherwise an M allocated at an
46-
// address like 0x123_0000_0000 might miss its wakeups.
4740

4841
// We use the uintptr mutex.key and note.key as a uint32.
4942
//
@@ -74,53 +67,18 @@ func lock2(l *mutex) {
7467

7568
timer := &lockTimer{lock: l}
7669
timer.begin()
77-
78-
// If a goroutine's stack needed to grow during a lock2 call, the M could
79-
// end up with two active lock2 calls (one each on curg and g0). If both are
80-
// contended, the call on g0 will corrupt mWaitList. Disable stack growth.
81-
stackguard0, throwsplit := gp.stackguard0, gp.throwsplit
82-
if gp == gp.m.curg {
83-
gp.stackguard0, gp.throwsplit = stackPreempt, true
84-
}
85-
8670
// On uniprocessors, no point spinning.
8771
// On multiprocessors, spin for ACTIVE_SPIN attempts.
8872
spin := 0
8973
if ncpu > 1 {
9074
spin = active_spin
9175
}
92-
var enqueued bool
9376
Loop:
9477
for i := 0; ; i++ {
9578
v := atomic.Loaduintptr(&l.key)
9679
if v&mutex_locked == 0 {
9780
// Unlocked. Try to lock.
9881
if atomic.Casuintptr(&l.key, v, v|mutex_locked) {
99-
// We now own the mutex
100-
v = v | mutex_locked
101-
for {
102-
old := v
103-
104-
head := muintptr(v &^ (mutex_sleeping | mutex_locked))
105-
fixMutexWaitList(head)
106-
if enqueued {
107-
head = removeMutexWaitList(head, gp.m)
108-
}
109-
110-
v = mutex_locked
111-
if head != 0 {
112-
v = v | uintptr(head) | mutex_sleeping
113-
}
114-
115-
if v == old || atomic.Casuintptr(&l.key, old, v) {
116-
gp.m.mWaitList.clearLinks()
117-
break
118-
}
119-
v = atomic.Loaduintptr(&l.key)
120-
}
121-
if gp == gp.m.curg {
122-
gp.stackguard0, gp.throwsplit = stackguard0, throwsplit
123-
}
12482
timer.end()
12583
return
12684
}
@@ -132,28 +90,21 @@ Loop:
13290
osyield()
13391
} else {
13492
// Someone else has it.
135-
// l->key points to a linked list of M's waiting
136-
// for this lock, chained through m->mWaitList.next.
137-
// Queue this M.
13893
for {
13994
head := v &^ (mutex_locked | mutex_sleeping)
140-
if !enqueued {
141-
gp.m.mWaitList.next = muintptr(head)
142-
head = uintptr(unsafe.Pointer(gp.m))
143-
if atomic.Casuintptr(&l.key, v, head|mutex_locked|mutex_sleeping) {
144-
enqueued = true
145-
break
146-
}
147-
gp.m.mWaitList.next = 0
95+
if atomic.Casuintptr(&l.key, v, head|mutex_locked|mutex_sleeping) {
96+
break
14897
}
14998
v = atomic.Loaduintptr(&l.key)
15099
if v&mutex_locked == 0 {
151100
continue Loop
152101
}
153102
}
154-
// Queued. Wait.
155-
futexsleep(key32(&l.key), uint32(v), -1)
156-
i = 0
103+
if v&mutex_locked != 0 {
104+
// Queued. Wait.
105+
futexsleep(key32(&l.key), uint32(v), -1)
106+
i = 0
107+
}
157108
}
158109
}
159110
}

src/runtime/lock_sema.go

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -54,49 +54,18 @@ func lock2(l *mutex) {
5454

5555
timer := &lockTimer{lock: l}
5656
timer.begin()
57-
58-
// If a goroutine's stack needed to grow during a lock2 call, the M could
59-
// end up with two active lock2 calls (one each on curg and g0). If both are
60-
// contended, the call on g0 will corrupt mWaitList. Disable stack growth.
61-
stackguard0, throwsplit := gp.stackguard0, gp.throwsplit
62-
if gp == gp.m.curg {
63-
gp.stackguard0, gp.throwsplit = stackPreempt, true
64-
}
65-
6657
// On uniprocessor's, no point spinning.
6758
// On multiprocessors, spin for ACTIVE_SPIN attempts.
6859
spin := 0
6960
if ncpu > 1 {
7061
spin = active_spin
7162
}
72-
var enqueued bool
7363
Loop:
7464
for i := 0; ; i++ {
7565
v := atomic.Loaduintptr(&l.key)
7666
if v&locked == 0 {
7767
// Unlocked. Try to lock.
7868
if atomic.Casuintptr(&l.key, v, v|locked) {
79-
// We now own the mutex
80-
v = v | locked
81-
for {
82-
old := v
83-
84-
head := muintptr(v &^ locked)
85-
fixMutexWaitList(head)
86-
if enqueued {
87-
head = removeMutexWaitList(head, gp.m)
88-
}
89-
v = locked | uintptr(head)
90-
91-
if v == old || atomic.Casuintptr(&l.key, old, v) {
92-
gp.m.mWaitList.clearLinks()
93-
break
94-
}
95-
v = atomic.Loaduintptr(&l.key)
96-
}
97-
if gp == gp.m.curg {
98-
gp.stackguard0, gp.throwsplit = stackguard0, throwsplit
99-
}
10069
timer.end()
10170
return
10271
}
@@ -112,29 +81,20 @@ Loop:
11281
// for this lock, chained through m.mWaitList.next.
11382
// Queue this M.
11483
for {
115-
if !enqueued {
116-
gp.m.mWaitList.next = muintptr(v &^ locked)
117-
if atomic.Casuintptr(&l.key, v, uintptr(unsafe.Pointer(gp.m))|locked) {
118-
enqueued = true
119-
break
120-
}
121-
gp.m.mWaitList.next = 0
84+
gp.m.mWaitList.next = muintptr(v &^ locked)
85+
if atomic.Casuintptr(&l.key, v, uintptr(unsafe.Pointer(gp.m))|locked) {
86+
break
12287
}
123-
12488
v = atomic.Loaduintptr(&l.key)
12589
if v&locked == 0 {
12690
continue Loop
12791
}
12892
}
129-
// Queued. Wait.
130-
semasleep(-1)
131-
i = 0
132-
enqueued = false
133-
// unlock2 removed this M from the list (it was at the head). We
134-
// need to erase the metadata about its former position in the
135-
// list -- and since it's no longer a published member we can do
136-
// so without races.
137-
gp.m.mWaitList.clearLinks()
93+
if v&locked != 0 {
94+
// Queued. Wait.
95+
semasleep(-1)
96+
i = 0
97+
}
13898
}
13999
}
140100
}

0 commit comments

Comments
 (0)