-
Notifications
You must be signed in to change notification settings - Fork 996
runtime (scheduler): generalize scheduler beyond timers #1006
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ func run() { | |
| initAll() | ||
| postinit() | ||
| callMain() | ||
| schedulerDone = true | ||
| }() | ||
| scheduler() | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| package runtime | ||
|
|
||
| import "internal/task" | ||
|
|
||
| var ( | ||
| sleepQueue *task.Task | ||
| sleepQueueBaseTime timeUnit | ||
| ) | ||
|
|
||
| // Add this task to the sleep queue, assuming its state is set to sleeping. | ||
| func addSleepTask(t *task.Task, duration int64) { | ||
| if schedulerDebug { | ||
| println(" set sleep:", t, uint(duration/tickMicros)) | ||
| if t.Next != nil { | ||
| panic("runtime: addSleepTask: expected next task to be nil") | ||
| } | ||
| } | ||
| t.Data = uint(duration / tickMicros) // TODO: longer durations | ||
| now := ticks() | ||
| if sleepQueue == nil { | ||
| scheduleLog(" -> sleep new queue") | ||
|
|
||
| // set new base time | ||
| sleepQueueBaseTime = now | ||
| } | ||
|
|
||
| // Add to sleep queue. | ||
| q := &sleepQueue | ||
| for ; *q != nil; q = &(*q).Next { | ||
| if t.Data < (*q).Data { | ||
| // this will finish earlier than the next - insert here | ||
| break | ||
| } else { | ||
| // this will finish later - adjust delay | ||
| t.Data -= (*q).Data | ||
| } | ||
| } | ||
| if *q != nil { | ||
| // cut delay time between this sleep task and the next | ||
| (*q).Data -= t.Data | ||
| } | ||
| t.Next = *q | ||
| *q = t | ||
| } | ||
|
|
||
| // pollSleepQueue checks the sleep queue to see if any tasks are now ready to run. | ||
| func pollSleepQueue(now timeUnit) bool { | ||
| // Add tasks that are done sleeping to the end of the runqueue so they | ||
| // will be executed soon. | ||
| var awoke bool | ||
| for sleepQueue != nil && now-sleepQueueBaseTime >= timeUnit(sleepQueue.Data) { | ||
| t := sleepQueue | ||
| scheduleLogTask(" awake:", t) | ||
| sleepQueueBaseTime += timeUnit(t.Data) | ||
| sleepQueue = t.Next | ||
| t.Next = nil | ||
| runqueue.Push(t) | ||
| awoke = true | ||
| } | ||
|
|
||
| return awoke | ||
| } | ||
|
|
||
| var curTime timeUnit | ||
niaow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // poll checks for any events that are ready and pushes them onto the runqueue. | ||
| // It returns true if any tasks were ready. | ||
| func poll() bool { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name of this function is rather broad. Perhaps something like |
||
| scheduleLog(" polling for events") | ||
| if sleepQueue == nil { | ||
| return false | ||
| } | ||
| curTime = ticks() | ||
| return pollSleepQueue(curTime) | ||
| } | ||
|
|
||
| // wait sleeps until any tasks are awoken by external events. | ||
| func wait() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same for this function. I think something like
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, #1040 adds an implementation for atsamd21 which directly uses |
||
| scheduleLog(" waiting for events") | ||
| if sleepQueue == nil { | ||
| // There are no timers, so timer wakeup is impossible. | ||
| if asyncScheduler { | ||
| // On WASM, sometimes callbacks are used to process wakeups from JS events. | ||
| // Therefore, we do not actually know if we deadlocked or not. | ||
| scheduleLog(" no tasks left!") | ||
| return | ||
| } | ||
| runtimePanic("deadlock") | ||
| } | ||
|
|
||
| // Sleep until the next timer hits. | ||
| timeLeft := timeUnit(sleepQueue.Data) - (curTime - sleepQueueBaseTime) | ||
| if schedulerDebug { | ||
| println(" sleeping...", sleepQueue, uint(timeLeft)) | ||
| for t := sleepQueue; t != nil; t = t.Next { | ||
| println(" task sleeping:", t, timeUnit(t.Data)) | ||
| } | ||
| } | ||
| sleepTicks(timeLeft) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not believe this sentence is true anymore with this PR: the scheduler runs until the main task has finished.