internal/task: swap stack chain when switching goroutines

This change swaps the stack chain when switching goroutines, ensuring that the chain is maintained consistently.
This is only really currently necessary with asyncify on wasm.
This commit is contained in:
Nia Waldvogel
2021-12-14 16:48:11 -05:00
committed by Ayke
parent d5c0083085
commit e4de7b4957
10 changed files with 51 additions and 4 deletions
+2 -1
View File
@@ -5,7 +5,8 @@ target triple = "wasm32-unknown-wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
%"internal/task.gcData" = type { i8* }
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
%"internal/task.stackState" = type { i32, i32 }
%runtime.chanSelectState = type { %runtime.channel*, i8* }
+2 -1
View File
@@ -5,7 +5,8 @@ target triple = "thumbv7m-unknown-unknown-eabi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
%"internal/task.gcData" = type {}
%"internal/task.state" = type { i32, i32* }
%runtime.chanSelectState = type { %runtime.channel*, i8* }
+2 -1
View File
@@ -5,7 +5,8 @@ target triple = "wasm32-unknown-wasi"
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
%"internal/task.gcData" = type { i8* }
%"internal/task.state" = type { i32, i8*, %"internal/task.stackState", i1 }
%"internal/task.stackState" = type { i32, i32 }
%runtime.chanSelectState = type { %runtime.channel*, i8* }
+2 -1
View File
@@ -6,7 +6,8 @@ target triple = "wasm32-unknown-wasi"
%runtime.funcValueWithSignature = type { i32, i8* }
%runtime.channel = type { i32, i32, i8, %runtime.channelBlockedList*, i32, i32, i32, i8* }
%runtime.channelBlockedList = type { %runtime.channelBlockedList*, %"internal/task.Task"*, %runtime.chanSelectState*, { %runtime.channelBlockedList*, i32, i32 } }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.state" }
%"internal/task.Task" = type { %"internal/task.Task"*, i8*, i64, %"internal/task.gcData", %"internal/task.state" }
%"internal/task.gcData" = type {}
%"internal/task.state" = type { i8* }
%runtime.chanSelectState = type { %runtime.channel*, i8* }
+19
View File
@@ -0,0 +1,19 @@
//go:build (gc.conservative || gc.extalloc) && tinygo.wasm && !scheduler.coroutines
// +build gc.conservative gc.extalloc
// +build tinygo.wasm
// +build !scheduler.coroutines
package task
import "unsafe"
//go:linkname swapStackChain runtime.swapStackChain
func swapStackChain(dst *unsafe.Pointer)
type gcData struct {
stackChain unsafe.Pointer
}
func (gcd *gcData) swap() {
swapStackChain(&gcd.stackChain)
}
+9
View File
@@ -0,0 +1,9 @@
//go:build (!gc.conservative && !gc.extalloc) || !tinygo.wasm || scheduler.coroutines
// +build !gc.conservative,!gc.extalloc !tinygo.wasm scheduler.coroutines
package task
type gcData struct{}
func (gcd *gcData) swap() {
}
+3
View File
@@ -15,6 +15,9 @@ type Task struct {
// Data is a field which can be used for storing state information.
Data uint64
// gcData holds data for the GC.
gcData gcData
// state is the underlying running state of the task.
state state
}
+2
View File
@@ -104,6 +104,7 @@ func (*stackState) unwind()
func (t *Task) Resume() {
// The current task must be saved and restored because this can nest on WASM with JS.
prevTask := currentTask
t.gcData.swap()
currentTask = t
if !t.state.launched {
t.state.launch()
@@ -112,6 +113,7 @@ func (t *Task) Resume() {
t.state.rewind()
}
currentTask = prevTask
t.gcData.swap()
if t.state.asyncifysp > t.state.csp {
runtimePanic("stack overflow")
}
+3
View File
@@ -1,3 +1,4 @@
//go:build scheduler.tasks
// +build scheduler.tasks
package task
@@ -54,7 +55,9 @@ func pause() {
// This may only be called from the scheduler.
func (t *Task) Resume() {
currentTask = t
t.gcData.swap()
t.state.resume()
t.gcData.swap()
currentTask = nil
}
+7
View File
@@ -1,3 +1,4 @@
//go:build (gc.conservative || gc.extalloc) && tinygo.wasm
// +build gc.conservative gc.extalloc
// +build tinygo.wasm
@@ -37,3 +38,9 @@ func markStack() {
// construction. Calls to it are later replaced with regular stack bookkeeping
// code.
func trackPointer(ptr unsafe.Pointer)
// swapStackChain swaps the stack chain.
// This is called from internal/task when switching goroutines.
func swapStackChain(dst **stackChainObject) {
*dst, stackChainStart = stackChainStart, *dst
}