Source file
src/runtime/sema.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package runtime
21
22 import (
23 "internal/cpu"
24 "internal/runtime/atomic"
25 "unsafe"
26 )
27
28
29
30
31
32
33
34
35
36
37
38
39
40 type semaRoot struct {
41 lock mutex
42 treap *sudog
43 nwait atomic.Uint32
44 }
45
46 var semtable semTable
47
48
49 const semTabSize = 251
50
51 type semTable [semTabSize]struct {
52 root semaRoot
53 pad [cpu.CacheLinePadSize - unsafe.Sizeof(semaRoot{})]byte
54 }
55
56 func (t *semTable) rootFor(addr *uint32) *semaRoot {
57 return &t[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
58 }
59
60
61
62
63
64
65
66
67
68
69
70 func sync_runtime_Semacquire(addr *uint32) {
71 semacquire1(addr, false, semaBlockProfile, 0, waitReasonSemacquire)
72 }
73
74
75 func poll_runtime_Semacquire(addr *uint32) {
76 semacquire1(addr, false, semaBlockProfile, 0, waitReasonSemacquire)
77 }
78
79
80
81
82
83
84
85
86
87
88
89 func sync_runtime_Semrelease(addr *uint32, handoff bool, skipframes int) {
90 semrelease1(addr, handoff, skipframes)
91 }
92
93
94 func internal_sync_runtime_SemacquireMutex(addr *uint32, lifo bool, skipframes int) {
95 semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile, skipframes, waitReasonSyncMutexLock)
96 }
97
98
99 func sync_runtime_SemacquireRWMutexR(addr *uint32, lifo bool, skipframes int) {
100 semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile, skipframes, waitReasonSyncRWMutexRLock)
101 }
102
103
104 func sync_runtime_SemacquireRWMutex(addr *uint32, lifo bool, skipframes int) {
105 semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile, skipframes, waitReasonSyncRWMutexLock)
106 }
107
108
109 func sync_runtime_SemacquireWaitGroup(addr *uint32, synctestDurable bool) {
110 reason := waitReasonSyncWaitGroupWait
111 if synctestDurable {
112 reason = waitReasonSynctestWaitGroupWait
113 }
114 semacquire1(addr, false, semaBlockProfile, 0, reason)
115 }
116
117
118 func poll_runtime_Semrelease(addr *uint32) {
119 semrelease(addr)
120 }
121
122
123 func internal_sync_runtime_Semrelease(addr *uint32, handoff bool, skipframes int) {
124 semrelease1(addr, handoff, skipframes)
125 }
126
127 func readyWithTime(s *sudog, traceskip int) {
128 if s.releasetime != 0 {
129 s.releasetime = cputicks()
130 }
131 goready(s.g, traceskip)
132 }
133
134 type semaProfileFlags int
135
136 const (
137 semaBlockProfile semaProfileFlags = 1 << iota
138 semaMutexProfile
139 )
140
141
142 func semacquire(addr *uint32) {
143 semacquire1(addr, false, 0, 0, waitReasonSemacquire)
144 }
145
146 func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags, skipframes int, reason waitReason) {
147 gp := getg()
148 if gp != gp.m.curg {
149 throw("semacquire not on the G stack")
150 }
151
152
153 if cansemacquire(addr) {
154 return
155 }
156
157
158
159
160
161
162
163 s := acquireSudog()
164 root := semtable.rootFor(addr)
165 t0 := int64(0)
166 s.releasetime = 0
167 s.acquiretime = 0
168 s.ticket = 0
169 if profile&semaBlockProfile != 0 && blockprofilerate > 0 {
170 t0 = cputicks()
171 s.releasetime = -1
172 }
173 if profile&semaMutexProfile != 0 && mutexprofilerate > 0 {
174 if t0 == 0 {
175 t0 = cputicks()
176 }
177 s.acquiretime = t0
178 }
179 for {
180 lockWithRank(&root.lock, lockRankRoot)
181
182 root.nwait.Add(1)
183
184 if cansemacquire(addr) {
185 root.nwait.Add(-1)
186 unlock(&root.lock)
187 break
188 }
189
190
191 root.queue(addr, s, lifo)
192 goparkunlock(&root.lock, reason, traceBlockSync, 4+skipframes)
193 if s.ticket != 0 || cansemacquire(addr) {
194 break
195 }
196 }
197 if s.releasetime > 0 {
198 blockevent(s.releasetime-t0, 3+skipframes)
199 }
200 releaseSudog(s)
201 }
202
203 func semrelease(addr *uint32) {
204 semrelease1(addr, false, 0)
205 }
206
207 func semrelease1(addr *uint32, handoff bool, skipframes int) {
208 root := semtable.rootFor(addr)
209 atomic.Xadd(addr, 1)
210
211
212
213
214 if root.nwait.Load() == 0 {
215 return
216 }
217
218
219 lockWithRank(&root.lock, lockRankRoot)
220 if root.nwait.Load() == 0 {
221
222
223 unlock(&root.lock)
224 return
225 }
226 s, t0, tailtime := root.dequeue(addr)
227 if s != nil {
228 root.nwait.Add(-1)
229 }
230 unlock(&root.lock)
231 if s != nil {
232 acquiretime := s.acquiretime
233 if acquiretime != 0 {
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249 dt0 := t0 - acquiretime
250 dt := dt0
251 if s.waiters != 0 {
252 dtail := t0 - tailtime
253 dt += (dtail + dt0) / 2 * int64(s.waiters)
254 }
255 mutexevent(dt, 3+skipframes)
256 }
257 if s.ticket != 0 {
258 throw("corrupted semaphore ticket")
259 }
260 if handoff && cansemacquire(addr) {
261 s.ticket = 1
262 }
263 readyWithTime(s, 5+skipframes)
264 if s.ticket == 1 && getg().m.locks == 0 && getg() != getg().m.g0 {
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286 goyield()
287 }
288 }
289 }
290
291 func cansemacquire(addr *uint32) bool {
292 for {
293 v := atomic.Load(addr)
294 if v == 0 {
295 return false
296 }
297 if atomic.Cas(addr, v, v-1) {
298 return true
299 }
300 }
301 }
302
303
304 func (root *semaRoot) queue(addr *uint32, s *sudog, lifo bool) {
305 s.g = getg()
306 s.elem.set(unsafe.Pointer(addr))
307
308
309 s.g.waiting = s
310 s.next = nil
311 s.prev = nil
312 s.waiters = 0
313
314 var last *sudog
315 pt := &root.treap
316 for t := *pt; t != nil; t = *pt {
317 if uintptr(unsafe.Pointer(addr)) == t.elem.uintptr() {
318
319 if lifo {
320
321 *pt = s
322 s.ticket = t.ticket
323 s.acquiretime = t.acquiretime
324 s.parent = t.parent
325 s.prev = t.prev
326 s.next = t.next
327 if s.prev != nil {
328 s.prev.parent = s
329 }
330 if s.next != nil {
331 s.next.parent = s
332 }
333
334 s.waitlink = t
335 s.waittail = t.waittail
336 if s.waittail == nil {
337 s.waittail = t
338 }
339 s.waiters = t.waiters
340 if s.waiters+1 != 0 {
341 s.waiters++
342 }
343 t.parent = nil
344 t.prev = nil
345 t.next = nil
346 t.waittail = nil
347 } else {
348
349 if t.waittail == nil {
350 t.waitlink = s
351 } else {
352 t.waittail.waitlink = s
353 }
354 t.waittail = s
355 s.waitlink = nil
356 if t.waiters+1 != 0 {
357 t.waiters++
358 }
359 }
360 return
361 }
362 last = t
363 if uintptr(unsafe.Pointer(addr)) < t.elem.uintptr() {
364 pt = &t.prev
365 } else {
366 pt = &t.next
367 }
368 }
369
370
371
372
373
374
375
376
377
378
379
380
381 s.ticket = cheaprand() | 1
382 s.parent = last
383 *pt = s
384
385
386 for s.parent != nil && s.parent.ticket > s.ticket {
387 if s.parent.prev == s {
388 root.rotateRight(s.parent)
389 } else {
390 if s.parent.next != s {
391 panic("semaRoot queue")
392 }
393 root.rotateLeft(s.parent)
394 }
395 }
396 }
397
398
399
400
401
402
403
404
405 func (root *semaRoot) dequeue(addr *uint32) (found *sudog, now, tailtime int64) {
406 ps := &root.treap
407 s := *ps
408
409 for ; s != nil; s = *ps {
410 if uintptr(unsafe.Pointer(addr)) == s.elem.uintptr() {
411 goto Found
412 }
413
414 if uintptr(unsafe.Pointer(addr)) < s.elem.uintptr() {
415 ps = &s.prev
416 } else {
417 ps = &s.next
418 }
419 }
420 return nil, 0, 0
421
422 Found:
423 now = int64(0)
424 if s.acquiretime != 0 {
425 now = cputicks()
426 }
427 if t := s.waitlink; t != nil {
428
429 *ps = t
430 t.ticket = s.ticket
431 t.parent = s.parent
432 t.prev = s.prev
433 if t.prev != nil {
434 t.prev.parent = t
435 }
436 t.next = s.next
437 if t.next != nil {
438 t.next.parent = t
439 }
440 if t.waitlink != nil {
441 t.waittail = s.waittail
442 } else {
443 t.waittail = nil
444 }
445 t.waiters = s.waiters
446 if t.waiters > 1 {
447 t.waiters--
448 }
449
450
451
452 t.acquiretime = now
453 tailtime = s.waittail.acquiretime
454 s.waittail.acquiretime = now
455 s.waitlink = nil
456 s.waittail = nil
457 } else {
458
459 for s.next != nil || s.prev != nil {
460 if s.next == nil || s.prev != nil && s.prev.ticket < s.next.ticket {
461 root.rotateRight(s)
462 } else {
463 root.rotateLeft(s)
464 }
465 }
466
467 if s.parent != nil {
468 if s.parent.prev == s {
469 s.parent.prev = nil
470 } else {
471 s.parent.next = nil
472 }
473 } else {
474 root.treap = nil
475 }
476 tailtime = s.acquiretime
477 }
478
479 s.g.waiting = nil
480 s.parent = nil
481 s.elem.set(nil)
482 s.next = nil
483 s.prev = nil
484 s.ticket = 0
485 return s, now, tailtime
486 }
487
488
489
490 func (root *semaRoot) rotateLeft(x *sudog) {
491
492 p := x.parent
493 y := x.next
494 b := y.prev
495
496 y.prev = x
497 x.parent = y
498 x.next = b
499 if b != nil {
500 b.parent = x
501 }
502
503 y.parent = p
504 if p == nil {
505 root.treap = y
506 } else if p.prev == x {
507 p.prev = y
508 } else {
509 if p.next != x {
510 throw("semaRoot rotateLeft")
511 }
512 p.next = y
513 }
514 }
515
516
517
518 func (root *semaRoot) rotateRight(y *sudog) {
519
520 p := y.parent
521 x := y.prev
522 b := x.next
523
524 x.next = y
525 y.parent = x
526 y.prev = b
527 if b != nil {
528 b.parent = y
529 }
530
531 x.parent = p
532 if p == nil {
533 root.treap = x
534 } else if p.prev == y {
535 p.prev = x
536 } else {
537 if p.next != y {
538 throw("semaRoot rotateRight")
539 }
540 p.next = x
541 }
542 }
543
544
545
546
547 type notifyList struct {
548
549
550 wait atomic.Uint32
551
552
553
554
555
556
557
558
559 notify uint32
560
561
562 lock mutex
563 head *sudog
564 tail *sudog
565 }
566
567
568
569 func less(a, b uint32) bool {
570 return int32(a-b) < 0
571 }
572
573
574
575
576
577
578 func notifyListAdd(l *notifyList) uint32 {
579
580
581 return l.wait.Add(1) - 1
582 }
583
584
585
586
587
588 func notifyListWait(l *notifyList, t uint32) {
589 lockWithRank(&l.lock, lockRankNotifyList)
590
591
592 if less(t, l.notify) {
593 unlock(&l.lock)
594 return
595 }
596
597
598 s := acquireSudog()
599 s.g = getg()
600
601
602 s.elem.set(unsafe.Pointer(l))
603 s.g.waiting = s
604 s.ticket = t
605 s.releasetime = 0
606 t0 := int64(0)
607 if blockprofilerate > 0 {
608 t0 = cputicks()
609 s.releasetime = -1
610 }
611 if l.tail == nil {
612 l.head = s
613 } else {
614 l.tail.next = s
615 }
616 l.tail = s
617 goparkunlock(&l.lock, waitReasonSyncCondWait, traceBlockCondWait, 3)
618 if t0 != 0 {
619 blockevent(s.releasetime-t0, 2)
620 }
621
622
623 s.g.waiting = nil
624 s.elem.set(nil)
625 releaseSudog(s)
626 }
627
628
629
630
631 func notifyListNotifyAll(l *notifyList) {
632
633
634 if l.wait.Load() == atomic.Load(&l.notify) {
635 return
636 }
637
638
639
640 lockWithRank(&l.lock, lockRankNotifyList)
641 s := l.head
642 l.head = nil
643 l.tail = nil
644
645
646
647
648
649 atomic.Store(&l.notify, l.wait.Load())
650 unlock(&l.lock)
651
652
653 for s != nil {
654 next := s.next
655 s.next = nil
656 if s.g.bubble != nil && getg().bubble != s.g.bubble {
657 println("semaphore wake of synctest goroutine", s.g.goid, "from outside bubble")
658 fatal("semaphore wake of synctest goroutine from outside bubble")
659 }
660 readyWithTime(s, 4)
661 s = next
662 }
663 }
664
665
666
667
668 func notifyListNotifyOne(l *notifyList) {
669
670
671 if l.wait.Load() == atomic.Load(&l.notify) {
672 return
673 }
674
675 lockWithRank(&l.lock, lockRankNotifyList)
676
677
678 t := l.notify
679 if t == l.wait.Load() {
680 unlock(&l.lock)
681 return
682 }
683
684
685 atomic.Store(&l.notify, t+1)
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700 for p, s := (*sudog)(nil), l.head; s != nil; p, s = s, s.next {
701 if s.ticket == t {
702 n := s.next
703 if p != nil {
704 p.next = n
705 } else {
706 l.head = n
707 }
708 if n == nil {
709 l.tail = p
710 }
711 unlock(&l.lock)
712 s.next = nil
713 if s.g.bubble != nil && getg().bubble != s.g.bubble {
714 println("semaphore wake of synctest goroutine", s.g.goid, "from outside bubble")
715 fatal("semaphore wake of synctest goroutine from outside bubble")
716 }
717 readyWithTime(s, 4)
718 return
719 }
720 }
721 unlock(&l.lock)
722 }
723
724
725 func notifyListCheck(sz uintptr) {
726 if sz != unsafe.Sizeof(notifyList{}) {
727 print("runtime: bad notifyList size - sync=", sz, " runtime=", unsafe.Sizeof(notifyList{}), "\n")
728 throw("bad notifyList size")
729 }
730 }
731
732
733 func internal_sync_nanotime() int64 {
734 return nanotime()
735 }
736
View as plain text