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