mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-13 15:33:40 +00:00
refactor coroutine lowering and tasks
This commit is contained in:
+31
-145
@@ -14,27 +14,16 @@ package runtime
|
||||
// of the coroutine-based scheduler, it is the coroutine pointer (a *i8 in
|
||||
// LLVM).
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"internal/task"
|
||||
)
|
||||
|
||||
const schedulerDebug = false
|
||||
|
||||
// State of a task. Internally represented as:
|
||||
//
|
||||
// {i8* next, i8* ptr, i32/i64 data}
|
||||
type taskState struct {
|
||||
next *task
|
||||
ptr unsafe.Pointer
|
||||
data uint
|
||||
}
|
||||
|
||||
// Queues used by the scheduler.
|
||||
//
|
||||
// TODO: runqueueFront can be removed by making the run queue a circular linked
|
||||
// list. The runqueueBack will simply refer to the front in the 'next' pointer.
|
||||
var (
|
||||
runqueueFront *task
|
||||
runqueueBack *task
|
||||
sleepQueue *task
|
||||
runqueue task.Queue
|
||||
sleepQueue *task.Task
|
||||
sleepQueueBaseTime timeUnit
|
||||
)
|
||||
|
||||
@@ -46,14 +35,14 @@ func scheduleLog(msg string) {
|
||||
}
|
||||
|
||||
// Simple logging with a task pointer, for debugging.
|
||||
func scheduleLogTask(msg string, t *task) {
|
||||
func scheduleLogTask(msg string, t *task.Task) {
|
||||
if schedulerDebug {
|
||||
println("---", msg, t)
|
||||
}
|
||||
}
|
||||
|
||||
// Simple logging with a channel and task pointer.
|
||||
func scheduleLogChan(msg string, ch *channel, t *task) {
|
||||
func scheduleLogChan(msg string, ch *channel, t *task.Task) {
|
||||
if schedulerDebug {
|
||||
println("---", msg, ch, t)
|
||||
}
|
||||
@@ -67,7 +56,7 @@ func scheduleLogChan(msg string, ch *channel, t *task) {
|
||||
//go:noinline
|
||||
func deadlock() {
|
||||
// call yield without requesting a wakeup
|
||||
yield()
|
||||
task.Pause()
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
@@ -80,122 +69,20 @@ func Goexit() {
|
||||
deadlock()
|
||||
}
|
||||
|
||||
// unblock unblocks a task and returns the next value
|
||||
func unblock(t *task) *task {
|
||||
state := t.state()
|
||||
next := state.next
|
||||
state.next = nil
|
||||
activateTask(t)
|
||||
return next
|
||||
}
|
||||
|
||||
// unblockChain unblocks the next task on the stack/queue, returning it
|
||||
// also updates the chain, putting the next element into the chain pointer
|
||||
// if the chain is used as a queue, tail is used as a pointer to the final insertion point
|
||||
// if the chain is used as a stack, tail should be nil
|
||||
func unblockChain(chain **task, tail ***task) *task {
|
||||
t := *chain
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
*chain = unblock(t)
|
||||
if tail != nil && *chain == nil {
|
||||
*tail = chain
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// dropChain drops a task from the given stack or queue
|
||||
// if the chain is used as a queue, tail is used as a pointer to the field containing a pointer to the next insertion point
|
||||
// if the chain is used as a stack, tail should be nil
|
||||
func dropChain(t *task, chain **task, tail ***task) {
|
||||
for c := chain; *c != nil; c = &((*c).state().next) {
|
||||
if *c == t {
|
||||
next := (*c).state().next
|
||||
if next == nil && tail != nil {
|
||||
*tail = c
|
||||
}
|
||||
*c = next
|
||||
return
|
||||
}
|
||||
}
|
||||
panic("runtime: task not in chain")
|
||||
}
|
||||
|
||||
// Pause the current task for a given time.
|
||||
//go:linkname sleep time.Sleep
|
||||
func sleep(duration int64) {
|
||||
addSleepTask(getCoroutine(), duration)
|
||||
yield()
|
||||
}
|
||||
|
||||
func avrSleep(duration int64) {
|
||||
sleepTicks(timeUnit(duration / tickMicros))
|
||||
}
|
||||
|
||||
// Add a non-queued task to the run queue.
|
||||
//
|
||||
// This is a compiler intrinsic, and is called from a callee to reactivate the
|
||||
// caller.
|
||||
func activateTask(t *task) {
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
scheduleLogTask(" set runnable:", t)
|
||||
runqueuePushBack(t)
|
||||
}
|
||||
|
||||
// getTaskStateData is a helper function to get the current .data field of the
|
||||
// goroutine state.
|
||||
//go:inline
|
||||
func getTaskStateData(t *task) uint {
|
||||
return t.state().data
|
||||
}
|
||||
|
||||
// Add this task to the end of the run queue. May also destroy the task if it's
|
||||
// done.
|
||||
func runqueuePushBack(t *task) {
|
||||
if schedulerDebug {
|
||||
scheduleLogTask(" pushing back:", t)
|
||||
if t.state().next != nil {
|
||||
panic("runtime: runqueuePushBack: expected next task to be nil")
|
||||
}
|
||||
}
|
||||
if runqueueBack == nil { // empty runqueue
|
||||
runqueueBack = t
|
||||
runqueueFront = t
|
||||
} else {
|
||||
lastTaskState := runqueueBack.state()
|
||||
lastTaskState.next = t
|
||||
runqueueBack = t
|
||||
}
|
||||
}
|
||||
|
||||
// Get a task from the front of the run queue. Returns nil if there is none.
|
||||
func runqueuePopFront() *task {
|
||||
t := runqueueFront
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
state := t.state()
|
||||
runqueueFront = state.next
|
||||
if runqueueFront == nil {
|
||||
// Runqueue is empty now.
|
||||
runqueueBack = nil
|
||||
}
|
||||
state.next = nil
|
||||
return t
|
||||
// Add this task to the end of the run queue.
|
||||
func runqueuePushBack(t *task.Task) {
|
||||
runqueue.Push(t)
|
||||
}
|
||||
|
||||
// Add this task to the sleep queue, assuming its state is set to sleeping.
|
||||
func addSleepTask(t *task, duration int64) {
|
||||
func addSleepTask(t *task.Task, duration int64) {
|
||||
if schedulerDebug {
|
||||
println(" set sleep:", t, uint(duration/tickMicros))
|
||||
if t.state().next != nil {
|
||||
if t.Next != nil {
|
||||
panic("runtime: addSleepTask: expected next task to be nil")
|
||||
}
|
||||
}
|
||||
t.state().data = uint(duration / tickMicros) // TODO: longer durations
|
||||
t.Data = uint(duration / tickMicros) // TODO: longer durations
|
||||
now := ticks()
|
||||
if sleepQueue == nil {
|
||||
scheduleLog(" -> sleep new queue")
|
||||
@@ -206,20 +93,20 @@ func addSleepTask(t *task, duration int64) {
|
||||
|
||||
// Add to sleep queue.
|
||||
q := &sleepQueue
|
||||
for ; *q != nil; q = &((*q).state()).next {
|
||||
if t.state().data < (*q).state().data {
|
||||
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.state().data -= (*q).state().data
|
||||
t.Data -= (*q).Data
|
||||
}
|
||||
}
|
||||
if *q != nil {
|
||||
// cut delay time between this sleep task and the next
|
||||
(*q).state().data -= t.state().data
|
||||
(*q).Data -= t.Data
|
||||
}
|
||||
t.state().next = *q
|
||||
t.Next = *q
|
||||
*q = t
|
||||
}
|
||||
|
||||
@@ -236,17 +123,16 @@ func scheduler() {
|
||||
|
||||
// Add tasks that are done sleeping to the end of the runqueue so they
|
||||
// will be executed soon.
|
||||
if sleepQueue != nil && now-sleepQueueBaseTime >= timeUnit(sleepQueue.state().data) {
|
||||
if sleepQueue != nil && now-sleepQueueBaseTime >= timeUnit(sleepQueue.Data) {
|
||||
t := sleepQueue
|
||||
scheduleLogTask(" awake:", t)
|
||||
state := t.state()
|
||||
sleepQueueBaseTime += timeUnit(state.data)
|
||||
sleepQueue = state.next
|
||||
state.next = nil
|
||||
runqueuePushBack(t)
|
||||
sleepQueueBaseTime += timeUnit(t.Data)
|
||||
sleepQueue = t.Next
|
||||
t.Next = nil
|
||||
runqueue.Push(t)
|
||||
}
|
||||
|
||||
t := runqueuePopFront()
|
||||
t := runqueue.Pop()
|
||||
if t == nil {
|
||||
if sleepQueue == nil {
|
||||
// No more tasks to execute.
|
||||
@@ -256,11 +142,11 @@ func scheduler() {
|
||||
scheduleLog(" no tasks left!")
|
||||
return
|
||||
}
|
||||
timeLeft := timeUnit(sleepQueue.state().data) - (now - sleepQueueBaseTime)
|
||||
timeLeft := timeUnit(sleepQueue.Data) - (now - sleepQueueBaseTime)
|
||||
if schedulerDebug {
|
||||
println(" sleeping...", sleepQueue, uint(timeLeft))
|
||||
for t := sleepQueue; t != nil; t = t.state().next {
|
||||
println(" task sleeping:", t, timeUnit(t.state().data))
|
||||
for t := sleepQueue; t != nil; t = t.Next {
|
||||
println(" task sleeping:", t, timeUnit(t.Data))
|
||||
}
|
||||
}
|
||||
sleepTicks(timeLeft)
|
||||
@@ -275,11 +161,11 @@ func scheduler() {
|
||||
|
||||
// Run the given task.
|
||||
scheduleLogTask(" run:", t)
|
||||
t.resume()
|
||||
t.Resume()
|
||||
}
|
||||
}
|
||||
|
||||
func Gosched() {
|
||||
runqueuePushBack(getCoroutine())
|
||||
yield()
|
||||
runqueue.Push(task.Current())
|
||||
task.Pause()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user