@@ -27,24 +27,19 @@ import (
27
27
28
28
// Asynchronous semaphore for sync.Mutex.
29
29
30
- // A semaRoot holds a linked list of sudog with distinct addresses (s.elem).
30
+ // A semaRoot holds a balanced tree of sudog with distinct addresses (s.elem).
31
31
// Each of those sudog may in turn point (through s.waitlink) to a list
32
32
// of other sudogs waiting on the same address.
33
33
// The operations on the inner lists of sudogs with the same address
34
- // are all O(1). Only the scanning of the top-level semaRoot list is O(n),
34
+ // are all O(1). The scanning of the top-level semaRoot list is O(log n),
35
35
// where n is the number of distinct addresses with goroutines blocked
36
36
// on them that hash to the given semaRoot.
37
- // In systems with many goroutines, most queue up on a few addresses,
38
- // so the linear search across unique addresses is probably OK.
39
- // At least, we'll use this until it's not.
40
- // The next step is probably to make the top-level list a treap instead
41
- // of a linked list.
42
37
// See golang.org/issue/17953 for a program that worked badly
43
- // before we introduced the second level of list.
38
+ // before we introduced the second level of list, and test/locklinear.go
39
+ // for a test that exercises this.
44
40
type semaRoot struct {
45
41
lock mutex
46
- head * sudog
47
- tail * sudog
42
+ treap * sudog // root of balanced tree of unique waiters.
48
43
nwait uint32 // Number of waiters. Read w/o the lock.
49
44
}
50
45
@@ -205,8 +200,12 @@ func cansemacquire(addr *uint32) bool {
205
200
func (root * semaRoot ) queue (addr * uint32 , s * sudog ) {
206
201
s .g = getg ()
207
202
s .elem = unsafe .Pointer (addr )
203
+ s .next = nil
204
+ s .prev = nil
208
205
209
- for t := root .head ; t != nil ; t = t .next {
206
+ var last * sudog
207
+ pt := & root .treap
208
+ for t := * pt ; t != nil ; t = * pt {
210
209
if t .elem == unsafe .Pointer (addr ) {
211
210
// Already have addr in list; add s to end of per-addr list.
212
211
if t .waittail == nil {
@@ -218,29 +217,55 @@ func (root *semaRoot) queue(addr *uint32, s *sudog) {
218
217
s .waitlink = nil
219
218
return
220
219
}
220
+ last = t
221
+ if uintptr (unsafe .Pointer (addr )) < uintptr (t .elem ) {
222
+ pt = & t .prev
223
+ } else {
224
+ pt = & t .next
225
+ }
221
226
}
222
227
223
- // Add s as new entry in list of unique addrs.
224
- s .next = nil
225
- s .prev = root .tail
226
- if root .tail != nil {
227
- root .tail .next = s
228
- } else {
229
- root .head = s
228
+ // Add s as new leaf in tree of unique addrs.
229
+ // The balanced tree is a treap using ticket as the random heap priority.
230
+ // That is, it is a binary tree ordered according to the elem addresses,
231
+ // but then among the space of possible binary trees respecting those
232
+ // addresses, it is kept balanced on average by maintaining a heap ordering
233
+ // on the ticket: s.ticket <= both s.prev.ticket and s.next.ticket.
234
+ // https://en.wikipedia.org/wiki/Treap
235
+ // http://faculty.washington.edu/aragon/pubs/rst89.pdf
236
+ s .ticket = fastrand ()
237
+ s .parent = last
238
+ * pt = s
239
+
240
+ // Rotate up into tree according to ticket (priority).
241
+ for s .parent != nil && s .parent .ticket > s .ticket {
242
+ if s .parent .prev == s {
243
+ root .rotateRight (s .parent )
244
+ } else {
245
+ if s .parent .next != s {
246
+ panic ("semaRoot queue" )
247
+ }
248
+ root .rotateLeft (s .parent )
249
+ }
230
250
}
231
- root .tail = s
232
251
}
233
252
234
253
// dequeue searches for and finds the first goroutine
235
254
// in semaRoot blocked on addr.
236
255
// If the sudog was being profiled, dequeue returns the time
237
256
// at which it was woken up as now. Otherwise now is 0.
238
257
func (root * semaRoot ) dequeue (addr * uint32 ) (found * sudog , now int64 ) {
239
- s := root .head
240
- for ; s != nil ; s = s .next {
258
+ ps := & root .treap
259
+ s := * ps
260
+ for ; s != nil ; s = * ps {
241
261
if s .elem == unsafe .Pointer (addr ) {
242
262
goto Found
243
263
}
264
+ if uintptr (unsafe .Pointer (addr )) < uintptr (s .elem ) {
265
+ ps = & s .prev
266
+ } else {
267
+ ps = & s .next
268
+ }
244
269
}
245
270
return nil , 0
246
271
@@ -250,18 +275,17 @@ Found:
250
275
now = cputicks ()
251
276
}
252
277
if t := s .waitlink ; t != nil {
253
- // Substitute t, also waiting on addr, for s in root list of unique addrs.
278
+ // Substitute t, also waiting on addr, for s in root tree of unique addrs.
279
+ * ps = t
280
+ t .ticket = s .ticket
281
+ t .parent = s .parent
254
282
t .prev = s .prev
255
- t .next = s .next
256
283
if t .prev != nil {
257
- t .prev .next = t
258
- } else {
259
- root .head = t
284
+ t .prev .parent = t
260
285
}
286
+ t .next = s .next
261
287
if t .next != nil {
262
- t .next .prev = t
263
- } else {
264
- root .tail = t
288
+ t .next .parent = t
265
289
}
266
290
if t .waitlink != nil {
267
291
t .waittail = s .waittail
@@ -272,24 +296,104 @@ Found:
272
296
s .waitlink = nil
273
297
s .waittail = nil
274
298
} else {
275
- // Remove s from list.
276
- if s .next != nil {
277
- s .next .prev = s .prev
278
- } else {
279
- root .tail = s .prev
299
+ // Rotate s down to be leaf of tree for removal, respecting priorities.
300
+ for s .next != nil || s .prev != nil {
301
+ if s .next == nil || s .prev != nil && s .prev .ticket < s .next .ticket {
302
+ root .rotateRight (s )
303
+ } else {
304
+ root .rotateLeft (s )
305
+ }
280
306
}
281
- if s .prev != nil {
282
- s .prev .next = s .next
307
+ // Remove s, now a leaf.
308
+ if s .parent != nil {
309
+ if s .parent .prev == s {
310
+ s .parent .prev = nil
311
+ } else {
312
+ s .parent .next = nil
313
+ }
283
314
} else {
284
- root .head = s . next
315
+ root .treap = nil
285
316
}
286
317
}
318
+ s .parent = nil
287
319
s .elem = nil
288
320
s .next = nil
289
321
s .prev = nil
290
322
return s , now
291
323
}
292
324
325
+ // rotateLeft rotates the tree rooted at node x.
326
+ // turning (x a (y b c)) into (y (x a b) c).
327
+ func (root * semaRoot ) rotateLeft (x * sudog ) {
328
+ // p -> (x a (y b c))
329
+ p := x .parent
330
+ a , y := x .prev , x .next
331
+ b , c := y .prev , y .next
332
+
333
+ y .prev = x
334
+ x .parent = y
335
+ y .next = c
336
+ if c != nil {
337
+ c .parent = y
338
+ }
339
+ x .prev = a
340
+ if a != nil {
341
+ a .parent = x
342
+ }
343
+ x .next = b
344
+ if b != nil {
345
+ b .parent = x
346
+ }
347
+
348
+ y .parent = p
349
+ if p == nil {
350
+ root .treap = y
351
+ } else if p .prev == x {
352
+ p .prev = y
353
+ } else {
354
+ if p .next != x {
355
+ throw ("semaRoot rotateLeft" )
356
+ }
357
+ p .next = y
358
+ }
359
+ }
360
+
361
+ // rotateRight rotates the tree rooted at node y.
362
+ // turning (y (x a b) c) into (x a (y b c)).
363
+ func (root * semaRoot ) rotateRight (y * sudog ) {
364
+ // p -> (y (x a b) c)
365
+ p := y .parent
366
+ x , c := y .prev , y .next
367
+ a , b := x .prev , x .next
368
+
369
+ x .prev = a
370
+ if a != nil {
371
+ a .parent = x
372
+ }
373
+ x .next = y
374
+ y .parent = x
375
+ y .prev = b
376
+ if b != nil {
377
+ b .parent = y
378
+ }
379
+ y .next = c
380
+ if c != nil {
381
+ c .parent = y
382
+ }
383
+
384
+ x .parent = p
385
+ if p == nil {
386
+ root .treap = x
387
+ } else if p .prev == y {
388
+ p .prev = x
389
+ } else {
390
+ if p .next != y {
391
+ throw ("semaRoot rotateRight" )
392
+ }
393
+ p .next = x
394
+ }
395
+ }
396
+
293
397
// notifyList is a ticket-based notification list used to implement sync.Cond.
294
398
//
295
399
// It must be kept in sync with the sync package.
@@ -414,10 +518,22 @@ func notifyListNotifyOne(l *notifyList) {
414
518
return
415
519
}
416
520
417
- // Update the next notify ticket number, and try to find the G that
418
- // needs to be notified. If it hasn't made it to the list yet we won't
419
- // find it, but it won't park itself once it sees the new notify number.
521
+ // Update the next notify ticket number.
420
522
atomic .Store (& l .notify , t + 1 )
523
+
524
+ // Try to find the g that needs to be notified.
525
+ // If it hasn't made it to the list yet we won't find it,
526
+ // but it won't park itself once it sees the new notify number.
527
+ //
528
+ // This scan looks linear but essentially always stops quickly.
529
+ // Because g's queue separately from taking numbers,
530
+ // there may be minor reorderings in the list, but we
531
+ // expect the g we're looking for to be near the front.
532
+ // The g has others in front of it on the list only to the
533
+ // extent that it lost the race, so the iteration will not
534
+ // be too long. This applies even when the g is missing:
535
+ // it hasn't yet gotten to sleep and has lost the race to
536
+ // the (few) other g's that we find on the list.
421
537
for p , s := (* sudog )(nil ), l .head ; s != nil ; p , s = s , s .next {
422
538
if s .ticket == t {
423
539
n := s .next
0 commit comments