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