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