v / vlib / goroutines / park.v
143 lines · 127 sloc · 3.43 KB · e21acca549c0df34c98beb389088dbf7cee4c4d0
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4//
5// Goroutine parking (blocking/unblocking) mechanism.
6// Translated from Go's gopark/goready in proc.go.
7//
8// When a goroutine needs to block (e.g., waiting on a channel),
9// it "parks" itself - yielding its M to the scheduler.
10// When the condition is met, another goroutine "readies" it
11// by putting it back on a run queue.
12module goroutines
13
14// gopark puts the current goroutine into a waiting state.
15// The goroutine can be made runnable again by calling goready.
16// reason describes why the goroutine is parking (for debugging).
17// Translated from Go's gopark() in proc.go.
18pub fn gopark(reason string) {
19 mut mp := get_current_m()
20 if mp == unsafe { nil } {
21 return
22 }
23 mut gp := mp.curg
24 if gp == unsafe { nil } {
25 return
26 }
27
28 gp.status = .waiting
29 gp.wait_reason = reason
30
31 // Dissociate G from M
32 mp.curg = unsafe { nil }
33 gp.m = unsafe { nil }
34
35 // Switch back to the scheduler (M's g0 context)
36 context_switch(mut &gp.context, &mp.g0.context)
37}
38
39// goready puts a waiting goroutine back on a run queue.
40// Translated from Go's goready() in proc.go.
41pub fn goready(gp &Goroutine) {
42 if gp == unsafe { nil } {
43 return
44 }
45 mut g := unsafe { gp }
46 if g.status != .waiting {
47 return
48 }
49 g.status = .runnable
50 g.wait_reason = ''
51
52 // Put it on the current P's local run queue, or global if no P
53 mut pp := get_current_p()
54 if pp != unsafe { nil } {
55 runq_put(mut pp, g, true)
56 } else {
57 glob_runq_put(g)
58 }
59
60 // Try to wake an idle P to run this goroutine
61 wake_p()
62}
63
64// gosched yields the processor, allowing other goroutines to run.
65// Translated from Go's Gosched() / goschedImpl() in proc.go.
66pub fn gosched() {
67 mut mp := get_current_m()
68 if mp == unsafe { nil } {
69 return
70 }
71 mut gp := mp.curg
72 if gp == unsafe { nil } {
73 return
74 }
75 mut pp := mp.p
76 if pp == unsafe { nil } {
77 return
78 }
79
80 // Put the current G back on the run queue as runnable
81 gp.status = .runnable
82 mp.curg = unsafe { nil }
83 gp.m = unsafe { nil }
84
85 // Put on local queue
86 runq_put(mut pp, gp, false)
87
88 // Switch back to scheduler
89 context_switch(mut &gp.context, &mp.g0.context)
90}
91
92// Sudog represents a G in a wait list (e.g., channel wait queue).
93// Translated from Go's sudog struct in runtime2.go.
94pub struct Sudog {
95pub mut:
96 g &Goroutine = unsafe { nil } // the waiting goroutine
97 next &Sudog = unsafe { nil } // next in wait list
98 prev &Sudog = unsafe { nil } // prev in wait list
99 elem voidptr // data element (may point to stack)
100 success bool // true if woken by successful channel op
101 c voidptr // channel pointer
102}
103
104// WaitQ is a wait queue of Sudogs (used by channels).
105// Translated from Go's waitq struct in runtime2.go.
106pub struct WaitQ {
107pub mut:
108 first &Sudog = unsafe { nil }
109 last &Sudog = unsafe { nil }
110}
111
112pub fn (mut q WaitQ) enqueue(s &Sudog) {
113 mut sg := unsafe { s }
114 sg.next = unsafe { nil }
115 sg.prev = q.last
116 if q.last != unsafe { nil } {
117 q.last.next = sg
118 } else {
119 q.first = sg
120 }
121 q.last = sg
122}
123
124pub fn (mut q WaitQ) dequeue() &Sudog {
125 sg := q.first
126 if sg == unsafe { nil } {
127 return unsafe { nil }
128 }
129 q.first = sg.next
130 if q.first != unsafe { nil } {
131 q.first.prev = unsafe { nil }
132 } else {
133 q.last = unsafe { nil }
134 }
135 mut s := unsafe { sg }
136 s.next = unsafe { nil }
137 s.prev = unsafe { nil }
138 return s
139}
140
141pub fn (q &WaitQ) empty() bool {
142 return q.first == unsafe { nil }
143}
144