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 = unsafe.Pointer(addr)
307 s.next = nil
308 s.prev = nil
309 s.waiters = 0
310
311 var last *sudog
312 pt := &root.treap
313 for t := *pt; t != nil; t = *pt {
314 if t.elem == unsafe.Pointer(addr) {
315
316 if lifo {
317
318 *pt = s
319 s.ticket = t.ticket
320 s.acquiretime = t.acquiretime
321 s.parent = t.parent
322 s.prev = t.prev
323 s.next = t.next
324 if s.prev != nil {
325 s.prev.parent = s
326 }
327 if s.next != nil {
328 s.next.parent = s
329 }
330
331 s.waitlink = t
332 s.waittail = t.waittail
333 if s.waittail == nil {
334 s.waittail = t
335 }
336 s.waiters = t.waiters
337 if s.waiters+1 != 0 {
338 s.waiters++
339 }
340 t.parent = nil
341 t.prev = nil
342 t.next = nil
343 t.waittail = nil
344 } else {
345
346 if t.waittail == nil {
347 t.waitlink = s
348 } else {
349 t.waittail.waitlink = s
350 }
351 t.waittail = s
352 s.waitlink = nil
353 if t.waiters+1 != 0 {
354 t.waiters++
355 }
356 }
357 return
358 }
359 last = t
360 if uintptr(unsafe.Pointer(addr)) < uintptr(t.elem) {
361 pt = &t.prev
362 } else {
363 pt = &t.next
364 }
365 }
366
367
368
369
370
371
372
373
374
375
376
377
378 s.ticket = cheaprand() | 1
379 s.parent = last
380 *pt = s
381
382
383 for s.parent != nil && s.parent.ticket > s.ticket {
384 if s.parent.prev == s {
385 root.rotateRight(s.parent)
386 } else {
387 if s.parent.next != s {
388 panic("semaRoot queue")
389 }
390 root.rotateLeft(s.parent)
391 }
392 }
393 }
394
395
396
397
398
399
400
401
402 func (root *semaRoot) dequeue(addr *uint32) (found *sudog, now, tailtime int64) {
403 ps := &root.treap
404 s := *ps
405 for ; s != nil; s = *ps {
406 if s.elem == unsafe.Pointer(addr) {
407 goto Found
408 }
409 if uintptr(unsafe.Pointer(addr)) < uintptr(s.elem) {
410 ps = &s.prev
411 } else {
412 ps = &s.next
413 }
414 }
415 return nil, 0, 0
416
417 Found:
418 now = int64(0)
419 if s.acquiretime != 0 {
420 now = cputicks()
421 }
422 if t := s.waitlink; t != nil {
423
424 *ps = t
425 t.ticket = s.ticket
426 t.parent = s.parent
427 t.prev = s.prev
428 if t.prev != nil {
429 t.prev.parent = t
430 }
431 t.next = s.next
432 if t.next != nil {
433 t.next.parent = t
434 }
435 if t.waitlink != nil {
436 t.waittail = s.waittail
437 } else {
438 t.waittail = nil
439 }
440 t.waiters = s.waiters
441 if t.waiters > 1 {
442 t.waiters--
443 }
444
445
446
447 t.acquiretime = now
448 tailtime = s.waittail.acquiretime
449 s.waittail.acquiretime = now
450 s.waitlink = nil
451 s.waittail = nil
452 } else {
453
454 for s.next != nil || s.prev != nil {
455 if s.next == nil || s.prev != nil && s.prev.ticket < s.next.ticket {
456 root.rotateRight(s)
457 } else {
458 root.rotateLeft(s)
459 }
460 }
461
462 if s.parent != nil {
463 if s.parent.prev == s {
464 s.parent.prev = nil
465 } else {
466 s.parent.next = nil
467 }
468 } else {
469 root.treap = nil
470 }
471 tailtime = s.acquiretime
472 }
473 s.parent = nil
474 s.elem = nil
475 s.next = nil
476 s.prev = nil
477 s.ticket = 0
478 return s, now, tailtime
479 }
480
481
482
483 func (root *semaRoot) rotateLeft(x *sudog) {
484
485 p := x.parent
486 y := x.next
487 b := y.prev
488
489 y.prev = x
490 x.parent = y
491 x.next = b
492 if b != nil {
493 b.parent = x
494 }
495
496 y.parent = p
497 if p == nil {
498 root.treap = y
499 } else if p.prev == x {
500 p.prev = y
501 } else {
502 if p.next != x {
503 throw("semaRoot rotateLeft")
504 }
505 p.next = y
506 }
507 }
508
509
510
511 func (root *semaRoot) rotateRight(y *sudog) {
512
513 p := y.parent
514 x := y.prev
515 b := x.next
516
517 x.next = y
518 y.parent = x
519 y.prev = b
520 if b != nil {
521 b.parent = y
522 }
523
524 x.parent = p
525 if p == nil {
526 root.treap = x
527 } else if p.prev == y {
528 p.prev = x
529 } else {
530 if p.next != y {
531 throw("semaRoot rotateRight")
532 }
533 p.next = x
534 }
535 }
536
537
538
539
540 type notifyList struct {
541
542
543 wait atomic.Uint32
544
545
546
547
548
549
550
551
552 notify uint32
553
554
555 lock mutex
556 head *sudog
557 tail *sudog
558 }
559
560
561
562 func less(a, b uint32) bool {
563 return int32(a-b) < 0
564 }
565
566
567
568
569
570
571 func notifyListAdd(l *notifyList) uint32 {
572
573
574 return l.wait.Add(1) - 1
575 }
576
577
578
579
580
581 func notifyListWait(l *notifyList, t uint32) {
582 lockWithRank(&l.lock, lockRankNotifyList)
583
584
585 if less(t, l.notify) {
586 unlock(&l.lock)
587 return
588 }
589
590
591 s := acquireSudog()
592 s.g = getg()
593 s.ticket = t
594 s.releasetime = 0
595 t0 := int64(0)
596 if blockprofilerate > 0 {
597 t0 = cputicks()
598 s.releasetime = -1
599 }
600 if l.tail == nil {
601 l.head = s
602 } else {
603 l.tail.next = s
604 }
605 l.tail = s
606 goparkunlock(&l.lock, waitReasonSyncCondWait, traceBlockCondWait, 3)
607 if t0 != 0 {
608 blockevent(s.releasetime-t0, 2)
609 }
610 releaseSudog(s)
611 }
612
613
614
615
616 func notifyListNotifyAll(l *notifyList) {
617
618
619 if l.wait.Load() == atomic.Load(&l.notify) {
620 return
621 }
622
623
624
625 lockWithRank(&l.lock, lockRankNotifyList)
626 s := l.head
627 l.head = nil
628 l.tail = nil
629
630
631
632
633
634 atomic.Store(&l.notify, l.wait.Load())
635 unlock(&l.lock)
636
637
638 for s != nil {
639 next := s.next
640 s.next = nil
641 if s.g.bubble != nil && getg().bubble != s.g.bubble {
642 println("semaphore wake of synctest goroutine", s.g.goid, "from outside bubble")
643 fatal("semaphore wake of synctest goroutine from outside bubble")
644 }
645 readyWithTime(s, 4)
646 s = next
647 }
648 }
649
650
651
652
653 func notifyListNotifyOne(l *notifyList) {
654
655
656 if l.wait.Load() == atomic.Load(&l.notify) {
657 return
658 }
659
660 lockWithRank(&l.lock, lockRankNotifyList)
661
662
663 t := l.notify
664 if t == l.wait.Load() {
665 unlock(&l.lock)
666 return
667 }
668
669
670 atomic.Store(&l.notify, t+1)
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685 for p, s := (*sudog)(nil), l.head; s != nil; p, s = s, s.next {
686 if s.ticket == t {
687 n := s.next
688 if p != nil {
689 p.next = n
690 } else {
691 l.head = n
692 }
693 if n == nil {
694 l.tail = p
695 }
696 unlock(&l.lock)
697 s.next = nil
698 if s.g.bubble != nil && getg().bubble != s.g.bubble {
699 println("semaphore wake of synctest goroutine", s.g.goid, "from outside bubble")
700 fatal("semaphore wake of synctest goroutine from outside bubble")
701 }
702 readyWithTime(s, 4)
703 return
704 }
705 }
706 unlock(&l.lock)
707 }
708
709
710 func notifyListCheck(sz uintptr) {
711 if sz != unsafe.Sizeof(notifyList{}) {
712 print("runtime: bad notifyList size - sync=", sz, " runtime=", unsafe.Sizeof(notifyList{}), "\n")
713 throw("bad notifyList size")
714 }
715 }
716
717
718 func internal_sync_nanotime() int64 {
719 return nanotime()
720 }
721
View as plain text