1
1
package runtime
2
2
3
- // This file implements the TinyGo scheduler. This scheduler is a very simple
4
- // cooperative round robin scheduler, with a runqueue that contains a linked
5
- // list of goroutines (tasks) that should be run next, in order of when they
6
- // were added to the queue (first-in, first-out). It also contains a sleep queue
7
- // with sleeping goroutines in order of when they should be re-activated.
8
- //
9
- // The scheduler is used both for the asyncify based scheduler and for the task
10
- // based scheduler. In both cases, the 'internal/task.Task' type is used to represent one
11
- // goroutine.
12
-
13
- import (
14
- "internal/task"
15
- "runtime/interrupt"
16
- )
3
+ import "internal/task"
17
4
18
5
const schedulerDebug = false
19
6
20
- // On JavaScript, we can't do a blocking sleep. Instead we have to return and
21
- // queue a new scheduler invocation using setTimeout.
22
- const asyncScheduler = GOOS == "js"
23
-
24
- var schedulerDone bool
25
-
26
- // Queues used by the scheduler.
27
- var (
28
- runqueue task.Queue
29
- sleepQueue * task.Task
30
- sleepQueueBaseTime timeUnit
31
- timerQueue * timerNode
32
- )
33
-
34
7
// Simple logging, for debugging.
35
8
func scheduleLog (msg string ) {
36
9
if schedulerDebug {
@@ -52,202 +25,12 @@ func scheduleLogChan(msg string, ch *channel, t *task.Task) {
52
25
}
53
26
}
54
27
55
- // deadlock is called when a goroutine cannot proceed any more, but is in theory
56
- // not exited (so deferred calls won't run). This can happen for example in code
57
- // like this, that blocks forever:
58
- //
59
- // select{}
60
- //
61
- //go:noinline
62
- func deadlock () {
63
- // call yield without requesting a wakeup
64
- task .Pause ()
65
- panic ("unreachable" )
66
- }
67
-
68
28
// Goexit terminates the currently running goroutine. No other goroutines are affected.
69
29
//
70
30
// Unlike the main Go implementation, no deferred calls will be run.
71
31
//
72
32
//go:inline
73
33
func Goexit () {
74
- // its really just a deadlock
34
+ // TODO: run deferred functions
75
35
deadlock ()
76
36
}
77
-
78
- // Add this task to the end of the run queue.
79
- func runqueuePushBack (t * task.Task ) {
80
- runqueue .Push (t )
81
- }
82
-
83
- // Add this task to the sleep queue, assuming its state is set to sleeping.
84
- func addSleepTask (t * task.Task , duration timeUnit ) {
85
- if schedulerDebug {
86
- println (" set sleep:" , t , duration )
87
- if t .Next != nil {
88
- panic ("runtime: addSleepTask: expected next task to be nil" )
89
- }
90
- }
91
- t .Data = uint64 (duration )
92
- now := ticks ()
93
- if sleepQueue == nil {
94
- scheduleLog (" -> sleep new queue" )
95
-
96
- // set new base time
97
- sleepQueueBaseTime = now
98
- }
99
-
100
- // Add to sleep queue.
101
- q := & sleepQueue
102
- for ; * q != nil ; q = & (* q ).Next {
103
- if t .Data < (* q ).Data {
104
- // this will finish earlier than the next - insert here
105
- break
106
- } else {
107
- // this will finish later - adjust delay
108
- t .Data -= (* q ).Data
109
- }
110
- }
111
- if * q != nil {
112
- // cut delay time between this sleep task and the next
113
- (* q ).Data -= t .Data
114
- }
115
- t .Next = * q
116
- * q = t
117
- }
118
-
119
- // addTimer adds the given timer node to the timer queue. It must not be in the
120
- // queue already.
121
- // This function is very similar to addSleepTask but for timerQueue instead of
122
- // sleepQueue.
123
- func addTimer (tim * timerNode ) {
124
- mask := interrupt .Disable ()
125
-
126
- // Add to timer queue.
127
- q := & timerQueue
128
- for ; * q != nil ; q = & (* q ).next {
129
- if tim .whenTicks () < (* q ).whenTicks () {
130
- // this will finish earlier than the next - insert here
131
- break
132
- }
133
- }
134
- tim .next = * q
135
- * q = tim
136
- interrupt .Restore (mask )
137
- }
138
-
139
- // removeTimer is the implementation of time.stopTimer. It removes a timer from
140
- // the timer queue, returning true if the timer is present in the timer queue.
141
- func removeTimer (tim * timer ) bool {
142
- removedTimer := false
143
- mask := interrupt .Disable ()
144
- for t := & timerQueue ; * t != nil ; t = & (* t ).next {
145
- if (* t ).timer == tim {
146
- scheduleLog ("removed timer" )
147
- * t = (* t ).next
148
- removedTimer = true
149
- break
150
- }
151
- }
152
- if ! removedTimer {
153
- scheduleLog ("did not remove timer" )
154
- }
155
- interrupt .Restore (mask )
156
- return removedTimer
157
- }
158
-
159
- // Run the scheduler until all tasks have finished.
160
- // There are a few special cases:
161
- // - When returnAtDeadlock is true, it also returns when there are no more
162
- // runnable goroutines.
163
- // - When using the asyncify scheduler, it returns when it has to wait
164
- // (JavaScript uses setTimeout so the scheduler must return to the JS
165
- // environment).
166
- func scheduler (returnAtDeadlock bool ) {
167
- // Main scheduler loop.
168
- var now timeUnit
169
- for ! schedulerDone {
170
- scheduleLog ("" )
171
- scheduleLog (" schedule" )
172
- if sleepQueue != nil || timerQueue != nil {
173
- now = ticks ()
174
- }
175
-
176
- // Add tasks that are done sleeping to the end of the runqueue so they
177
- // will be executed soon.
178
- if sleepQueue != nil && now - sleepQueueBaseTime >= timeUnit (sleepQueue .Data ) {
179
- t := sleepQueue
180
- scheduleLogTask (" awake:" , t )
181
- sleepQueueBaseTime += timeUnit (t .Data )
182
- sleepQueue = t .Next
183
- t .Next = nil
184
- runqueue .Push (t )
185
- }
186
-
187
- // Check for expired timers to trigger.
188
- if timerQueue != nil && now >= timerQueue .whenTicks () {
189
- scheduleLog ("--- timer awoke" )
190
- delay := ticksToNanoseconds (now - timerQueue .whenTicks ())
191
- // Pop timer from queue.
192
- tn := timerQueue
193
- timerQueue = tn .next
194
- tn .next = nil
195
- // Run the callback stored in this timer node.
196
- tn .callback (tn , delay )
197
- }
198
-
199
- t := runqueue .Pop ()
200
- if t == nil {
201
- if sleepQueue == nil && timerQueue == nil {
202
- if returnAtDeadlock {
203
- return
204
- }
205
- if asyncScheduler {
206
- // JavaScript is treated specially, see below.
207
- return
208
- }
209
- waitForEvents ()
210
- continue
211
- }
212
-
213
- var timeLeft timeUnit
214
- if sleepQueue != nil {
215
- timeLeft = timeUnit (sleepQueue .Data ) - (now - sleepQueueBaseTime )
216
- }
217
- if timerQueue != nil {
218
- timeLeftForTimer := timerQueue .whenTicks () - now
219
- if sleepQueue == nil || timeLeftForTimer < timeLeft {
220
- timeLeft = timeLeftForTimer
221
- }
222
- }
223
-
224
- if schedulerDebug {
225
- println (" sleeping..." , sleepQueue , uint (timeLeft ))
226
- for t := sleepQueue ; t != nil ; t = t .Next {
227
- println (" task sleeping:" , t , timeUnit (t .Data ))
228
- }
229
- for tim := timerQueue ; tim != nil ; tim = tim .next {
230
- println ("--- timer waiting:" , tim , tim .whenTicks ())
231
- }
232
- }
233
- sleepTicks (timeLeft )
234
- if asyncScheduler {
235
- // The sleepTicks function above only sets a timeout at which
236
- // point the scheduler will be called again. It does not really
237
- // sleep. So instead of sleeping, we return and expect to be
238
- // called again.
239
- break
240
- }
241
- continue
242
- }
243
-
244
- // Run the given task.
245
- scheduleLogTask (" run:" , t )
246
- t .Resume ()
247
- }
248
- }
249
-
250
- func Gosched () {
251
- runqueue .Push (task .Current ())
252
- task .Pause ()
253
- }
0 commit comments