mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-16 18:53:29 +00:00
refactor coroutine lowering and tasks
This commit is contained in:
@@ -2,103 +2,8 @@
|
||||
|
||||
package runtime
|
||||
|
||||
// This file implements the Go scheduler using coroutines.
|
||||
// A goroutine contains a whole stack. A coroutine is just a single function.
|
||||
// How do we use coroutines for goroutines, then?
|
||||
// * Every function that contains a blocking call (like sleep) is marked
|
||||
// blocking, and all it's parents (callers) are marked blocking as well
|
||||
// transitively until the root (main.main or a go statement).
|
||||
// * A blocking function that calls a non-blocking function is called as
|
||||
// usual.
|
||||
// * A blocking function that calls a blocking function passes its own
|
||||
// coroutine handle as a parameter to the subroutine. When the subroutine
|
||||
// returns, it will re-insert the parent into the scheduler.
|
||||
// Note that we use the type 'task' to refer to a coroutine, for compatibility
|
||||
// with the task-based scheduler. A task type here does not represent the whole
|
||||
// task, but just the topmost coroutine. For most of the scheduler, this
|
||||
// difference doesn't matter.
|
||||
//
|
||||
// For more background on coroutines in LLVM:
|
||||
// https://llvm.org/docs/Coroutines.html
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// A coroutine instance, wrapped here to provide some type safety. The value
|
||||
// must not be used directly, it is meant to be used as an opaque *i8 in LLVM.
|
||||
type task uint8
|
||||
|
||||
//go:export llvm.coro.resume
|
||||
func (t *task) resume()
|
||||
|
||||
//go:export llvm.coro.destroy
|
||||
func (t *task) destroy()
|
||||
|
||||
//go:export llvm.coro.done
|
||||
func (t *task) done() bool
|
||||
|
||||
//go:export llvm.coro.promise
|
||||
func (t *task) _promise(alignment int32, from bool) unsafe.Pointer
|
||||
|
||||
// Get the state belonging to a task.
|
||||
func (t *task) state() *taskState {
|
||||
return (*taskState)(t._promise(int32(unsafe.Alignof(taskState{})), false))
|
||||
}
|
||||
|
||||
func makeGoroutine(uintptr) uintptr
|
||||
|
||||
// Compiler stub to get the current goroutine. Calls to this function are
|
||||
// removed in the goroutine lowering pass.
|
||||
func getCoroutine() *task
|
||||
|
||||
// setTaskStatePtr is a helper function to set the current .ptr field of a
|
||||
// coroutine promise.
|
||||
func setTaskStatePtr(t *task, value unsafe.Pointer) {
|
||||
t.state().ptr = value
|
||||
}
|
||||
|
||||
// getTaskStatePtr is a helper function to get the current .ptr field from a
|
||||
// coroutine promise.
|
||||
func getTaskStatePtr(t *task) unsafe.Pointer {
|
||||
if t == nil {
|
||||
blockingPanic()
|
||||
}
|
||||
return t.state().ptr
|
||||
}
|
||||
|
||||
// yield suspends execution of the current goroutine
|
||||
// any wakeups must be configured before calling yield
|
||||
func yield()
|
||||
|
||||
// getSystemStackPointer returns the current stack pointer of the system stack.
|
||||
// This is always the current stack pointer.
|
||||
func getSystemStackPointer() uintptr {
|
||||
return getCurrentStackPointer()
|
||||
}
|
||||
|
||||
func fakeCoroutine(dst **task) {
|
||||
*dst = getCoroutine()
|
||||
for {
|
||||
yield()
|
||||
}
|
||||
}
|
||||
|
||||
func getFakeCoroutine() *task {
|
||||
// this isnt defined behavior, but this is what our implementation does
|
||||
// this is really a horrible hack
|
||||
var t *task
|
||||
go fakeCoroutine(&t)
|
||||
|
||||
// the first line of fakeCoroutine will have completed by now
|
||||
return t
|
||||
}
|
||||
|
||||
// noret is a placeholder that can be used to indicate that an async function is not going to directly return here
|
||||
func noret()
|
||||
|
||||
func getParentHandle() *task
|
||||
|
||||
func llvmCoroRefHolder() {
|
||||
noret()
|
||||
getParentHandle()
|
||||
getCoroutine()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user