refactor coroutine lowering and tasks

This commit is contained in:
Jaden Weiss
2020-01-03 00:23:37 -05:00
committed by Ayke
parent 2521cacb51
commit 6a50f25a48
40 changed files with 1830 additions and 1515 deletions
-104
View File
@@ -2,110 +2,6 @@
package runtime
import "unsafe"
const stackSize = 1024
// Stack canary, to detect a stack overflow. The number is a random number
// generated by random.org. The bit fiddling dance is necessary because
// otherwise Go wouldn't allow the cast to a smaller integer size.
const stackCanary = uintptr(uint64(0x670c1333b83bf575) & uint64(^uintptr(0)))
var (
currentTask *task // currently running goroutine, or nil
)
// This type points to the bottom of the goroutine stack and contains some state
// that must be kept with the task. The last field is a canary, which is
// necessary to make sure that no stack overflow occured when switching tasks.
type task struct {
// The order of fields in this structs must be kept in sync with assembly!
calleeSavedRegs
pc uintptr
sp uintptr
taskState
canaryPtr *uintptr // used to detect stack overflows
}
// getCoroutine returns the currently executing goroutine. It is used as an
// intrinsic when compiling channel operations, but is not necessary with the
// task-based scheduler.
//go:inline
func getCoroutine() *task {
return currentTask
}
// state is a small helper that returns the task state, and is provided for
// compatibility with the coroutine implementation.
//go:inline
func (t *task) state() *taskState {
return &t.taskState
}
// resume is a small helper that resumes this task until this task switches back
// to the scheduler.
func (t *task) resume() {
currentTask = t
switchToTask(t)
currentTask = nil
}
// switchToScheduler saves the current state on the stack, saves the current
// stack pointer in the task, and switches to the scheduler. It must only be
// called when actually running on this task.
// When it returns, the scheduler has switched back to this task (for example,
// after a blocking operation completed).
//export tinygo_switchToScheduler
func switchToScheduler(t *task)
// switchToTask switches from the scheduler to the task. It must only be called
// from the scheduler.
// When this function returns, the task just yielded control back to the
// scheduler.
//export tinygo_switchToTask
func switchToTask(t *task)
// startTask is a small wrapper function that sets up the first (and only)
// argument to the new goroutine and makes sure it is exited when the goroutine
// finishes.
//go:extern tinygo_startTask
var startTask [0]uint8
// startGoroutine starts a new goroutine with the given function pointer and
// argument. It creates a new goroutine stack, prepares it for execution, and
// adds it to the runqueue.
func startGoroutine(fn, args uintptr) {
stack := alloc(stackSize)
t := (*task)(unsafe.Pointer(uintptr(stack) + stackSize - unsafe.Sizeof(task{})))
// Set up the stack canary, a random number that should be checked when
// switching from the task back to the scheduler. The stack canary pointer
// points to the first word of the stack. If it has changed between now and
// the next stack switch, there was a stack overflow.
t.canaryPtr = (*uintptr)(unsafe.Pointer(stack))
*t.canaryPtr = stackCanary
// Store the initial sp/pc for the startTask function (implemented in
// assembly).
t.sp = uintptr(stack) + stackSize - unsafe.Sizeof(task{})
t.pc = uintptr(unsafe.Pointer(&startTask))
t.prepareStartTask(fn, args)
scheduleLogTask(" start goroutine:", t)
runqueuePushBack(t)
}
// yield suspends execution of the current goroutine
// any wakeups must be configured before calling yield
//export runtime.yield
func yield() {
// Check whether the canary (the lowest address of the stack) is still
// valid. If it is not, a stack overflow has occured.
if *currentTask.canaryPtr != stackCanary {
runtimePanic("goroutine stack overflow")
}
switchToScheduler(currentTask)
}
// getSystemStackPointer returns the current stack pointer of the system stack.
// This is not necessarily the same as the current stack pointer.
//export tinygo_getSystemStackPointer