11package runtime
22
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"
174
185const schedulerDebug = false
196
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-
247var mainExited bool
258
26- // Queues used by the scheduler.
27- var (
28- runqueue task.Queue
29- sleepQueue * task.Task
30- sleepQueueBaseTime timeUnit
31- timerQueue * timerNode
32- )
33-
349// Simple logging, for debugging.
3510func scheduleLog (msg string ) {
3611 if schedulerDebug {
@@ -52,204 +27,12 @@ func scheduleLogChan(msg string, ch *channel, t *task.Task) {
5227 }
5328}
5429
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-
6830// Goexit terminates the currently running goroutine. No other goroutines are affected.
6931//
7032// Unlike the main Go implementation, no deferred calls will be run.
7133//
7234//go:inline
7335func Goexit () {
74- // its really just a deadlock
36+ // TODO: run deferred functions
7537 deadlock ()
7638}
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 ! mainExited {
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- if timeLeft > 0 {
234- sleepTicks (timeLeft )
235- if asyncScheduler {
236- // The sleepTicks function above only sets a timeout at
237- // which point the scheduler will be called again. It does
238- // not really sleep. So instead of sleeping, we return and
239- // expect to be called again.
240- break
241- }
242- }
243- continue
244- }
245-
246- // Run the given task.
247- scheduleLogTask (" run:" , t )
248- t .Resume ()
249- }
250- }
251-
252- func Gosched () {
253- runqueue .Push (task .Current ())
254- task .Pause ()
255- }
0 commit comments