mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 10:37:46 +00:00
a21a039ac7
This is a big change that will determine the stack size for many goroutines automatically. Functions that aren't recursive and don't call function pointers can in many cases have an automatically determined worst case stack size. This is useful, as the stack size is usually much lower than the previous hardcoded default of 1024 bytes: somewhere around 200-500 bytes is common. A side effect of this change is that the default stack sizes (including the stack size for other architectures such as AVR) can now be changed in the config JSON file, making it tunable per application.
104 lines
2.8 KiB
Go
104 lines
2.8 KiB
Go
// +build scheduler.coroutines
|
|
|
|
package task
|
|
|
|
import (
|
|
"unsafe"
|
|
)
|
|
|
|
// rawState is an underlying coroutine state exposed by llvm.coro.
|
|
// This matches *i8 in LLVM.
|
|
type rawState uint8
|
|
|
|
//export llvm.coro.resume
|
|
func (s *rawState) resume()
|
|
|
|
type state struct{ *rawState }
|
|
|
|
//export llvm.coro.noop
|
|
func noopState() *rawState
|
|
|
|
// Resume the task until it pauses or completes.
|
|
func (t *Task) Resume() {
|
|
t.state.resume()
|
|
}
|
|
|
|
// setState is used by the compiler to set the state of the function at the beginning of a function call.
|
|
// Returns the state of the caller.
|
|
func (t *Task) setState(s *rawState) *rawState {
|
|
caller := t.state
|
|
t.state = state{s}
|
|
return caller.rawState
|
|
}
|
|
|
|
// returnTo is used by the compiler to return to the state of the caller.
|
|
func (t *Task) returnTo(parent *rawState) {
|
|
t.state = state{parent}
|
|
t.returnCurrent()
|
|
}
|
|
|
|
// returnCurrent is used by the compiler to return to the state of the caller in a case where the state is not replaced.
|
|
func (t *Task) returnCurrent() {
|
|
scheduleTask(t)
|
|
}
|
|
|
|
//go:linkname scheduleTask runtime.runqueuePushBack
|
|
func scheduleTask(*Task)
|
|
|
|
// setReturnPtr is used by the compiler to store the return buffer into the task.
|
|
// This buffer is where the return value of a function that is about to be called will be stored.
|
|
func (t *Task) setReturnPtr(buf unsafe.Pointer) {
|
|
t.Ptr = buf
|
|
}
|
|
|
|
// getReturnPtr is used by the compiler to get the return buffer stored into the task.
|
|
// This is called at the beginning of an async function, and the return is stored into this buffer immediately before resuming the caller.
|
|
func (t *Task) getReturnPtr() unsafe.Pointer {
|
|
return t.Ptr
|
|
}
|
|
|
|
// createTask returns a new task struct initialized with a no-op state.
|
|
func createTask() *Task {
|
|
return &Task{
|
|
state: state{noopState()},
|
|
}
|
|
}
|
|
|
|
// start invokes a function in a new goroutine. Calls to this are inserted by the compiler.
|
|
// The created goroutine starts running immediately.
|
|
// This is implemented inside the compiler.
|
|
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr)
|
|
|
|
// Current returns the current active task.
|
|
// This is implemented inside the compiler.
|
|
func Current() *Task
|
|
|
|
// Pause suspends the current running task.
|
|
// This is implemented inside the compiler.
|
|
func Pause()
|
|
|
|
type taskHolder interface {
|
|
setState(*rawState) *rawState
|
|
returnTo(*rawState)
|
|
returnCurrent()
|
|
setReturnPtr(unsafe.Pointer)
|
|
getReturnPtr() unsafe.Pointer
|
|
}
|
|
|
|
// If there are no direct references to the task methods, they will not be discovered by the compiler, and this will trigger a compiler error.
|
|
// Instantiating this interface forces discovery of these methods.
|
|
var _ = taskHolder((*Task)(nil))
|
|
|
|
func fake() {
|
|
// Hack to ensure intrinsics are discovered.
|
|
Current()
|
|
go func() {}()
|
|
Pause()
|
|
}
|
|
|
|
// OnSystemStack returns whether the caller is running on the system stack.
|
|
func OnSystemStack() bool {
|
|
// This scheduler does not do any stack switching.
|
|
return true
|
|
}
|