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