Skip to content

Commit 6fb2a43

Browse files
committed
runtime: release completed Asyncify stacks
1 parent 931779e commit 6fb2a43

13 files changed

Lines changed: 69 additions & 16 deletions

File tree

compiler/goroutine.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,10 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
309309
}
310310
defer b.Dispose()
311311

312-
var deadlock llvm.Value
313-
var deadlockType llvm.Type
312+
var taskExit llvm.Value
313+
var taskExitType llvm.Type
314314
if c.Scheduler == "asyncify" {
315-
deadlockType, deadlock = c.getFunction(c.program.ImportedPackage("runtime").Members["deadlock"].(*ssa.Function))
315+
taskExitType, taskExit = c.getFunction(c.program.ImportedPackage("internal/task").Members["Exit"].(*ssa.Function))
316316
}
317317

318318
if !fn.IsAFunction().IsNil() {
@@ -377,7 +377,7 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
377377
b.CreateCall(fnType, fn, params, "")
378378

379379
if c.Scheduler == "asyncify" {
380-
b.CreateCall(deadlockType, deadlock, []llvm.Value{
380+
b.CreateCall(taskExitType, taskExit, []llvm.Value{
381381
llvm.Undef(c.dataPtrType),
382382
}, "")
383383
}
@@ -528,14 +528,14 @@ func (c *compilerContext) createGoroutineStartWrapper(fnType llvm.Type, fn llvm.
528528
b.CreateCall(fnType, fnPtr, params, "")
529529

530530
if c.Scheduler == "asyncify" {
531-
b.CreateCall(deadlockType, deadlock, []llvm.Value{
531+
b.CreateCall(taskExitType, taskExit, []llvm.Value{
532532
llvm.Undef(c.dataPtrType),
533533
}, "")
534534
}
535535
}
536536

537537
if c.Scheduler == "asyncify" {
538-
// The goroutine was terminated via deadlock.
538+
// The goroutine was terminated by Exit.
539539
b.CreateUnreachable()
540540
} else {
541541
// Finish the function. Every basic block must end in a terminator, and

compiler/testdata/goroutine-wasm-asyncify.ll

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ entry:
2222

2323
declare void @main.regularFunction(i32, ptr) #0
2424

25-
declare void @runtime.deadlock(ptr) #0
25+
declare void @"internal/task.Exit"(ptr) #0
2626

2727
; Function Attrs: nounwind
2828
define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #2 {
2929
entry:
3030
%unpack.int = ptrtoint ptr %0 to i32
3131
call void @main.regularFunction(i32 %unpack.int, ptr undef) #11
32-
call void @runtime.deadlock(ptr undef) #11
32+
call void @"internal/task.Exit"(ptr undef) #11
3333
unreachable
3434
}
3535

@@ -53,7 +53,7 @@ define linkonce_odr void @"main.inlineFunctionGoroutine$1$gowrapper"(ptr %0) unn
5353
entry:
5454
%unpack.int = ptrtoint ptr %0 to i32
5555
call void @"main.inlineFunctionGoroutine$1"(i32 %unpack.int, ptr undef)
56-
call void @runtime.deadlock(ptr undef) #11
56+
call void @"internal/task.Exit"(ptr undef) #11
5757
unreachable
5858
}
5959

@@ -96,7 +96,7 @@ entry:
9696
%2 = getelementptr inbounds nuw i8, ptr %0, i32 4
9797
%3 = load ptr, ptr %2, align 4
9898
call void @"main.closureFunctionGoroutine$1"(i32 %1, ptr %3)
99-
call void @runtime.deadlock(ptr undef) #11
99+
call void @"internal/task.Exit"(ptr undef) #11
100100
unreachable
101101
}
102102

@@ -130,7 +130,7 @@ entry:
130130
%4 = getelementptr inbounds nuw i8, ptr %0, i32 8
131131
%5 = load ptr, ptr %4, align 4
132132
call void %5(i32 %1, ptr %3) #11
133-
call void @runtime.deadlock(ptr undef) #11
133+
call void @"internal/task.Exit"(ptr undef) #11
134134
unreachable
135135
}
136136

@@ -193,7 +193,7 @@ entry:
193193
%6 = getelementptr inbounds nuw i8, ptr %0, i32 12
194194
%7 = load ptr, ptr %6, align 4
195195
call void @"interface:{Print:func:{basic:string}{}}.Print$invoke"(ptr %1, ptr %3, i32 %5, ptr %7, ptr undef) #11
196-
call void @runtime.deadlock(ptr undef) #11
196+
call void @"internal/task.Exit"(ptr undef) #11
197197
unreachable
198198
}
199199

compiler/testdata/large.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,13 +298,13 @@ entry:
298298
ret void
299299
}
300300

301-
declare void @runtime.deadlock(ptr) #0
301+
declare void @"internal/task.Exit"(ptr) #0
302302

303303
; Function Attrs: nounwind
304304
define linkonce_odr void @"main.readLargeValue$gowrapper"(ptr %0) unnamed_addr #10 {
305305
entry:
306306
%1 = call i8 @main.readLargeValue(ptr %0, ptr undef)
307-
call void @runtime.deadlock(ptr undef) #13
307+
call void @"internal/task.Exit"(ptr undef) #13
308308
unreachable
309309
}
310310

main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,7 @@ func TestGoexitCrash(t *testing.T) {
10631063
panicStrategy string
10641064
want string
10651065
}{
1066-
{"wasip1-deadlock", "deadlock", "", "deadlocked: no event source"},
1066+
{"wasip1-deadlock", "deadlock", "", "fatal error: all goroutines are asleep - deadlock!"},
10671067
{"wasip1-goexit-panic-trap", "defer", "trap", "defer ran"},
10681068
} {
10691069
t.Run(tc.name, func(t *testing.T) {

src/internal/task/task.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ type Task struct {
3030
// since it falls into the padding of the FipsIndicator bit above.
3131
RunState uint8
3232

33+
// Exited is set after a task with a releasable stack has finished.
34+
Exited bool
35+
3336
// DeferFrame stores a pointer to the (stack allocated) defer frame of the
3437
// goroutine that is used for the recover builtin.
3538
DeferFrame unsafe.Pointer
@@ -72,5 +75,8 @@ func getGoroutineStackSize(fn uintptr) uintptr
7275
//go:linkname runtime_alloc runtime.alloc
7376
func runtime_alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
7477

78+
//go:linkname runtime_freeTaskStack runtime.freeTaskStack
79+
func runtime_freeTaskStack(ptr unsafe.Pointer)
80+
7581
//go:linkname scheduleTask runtime.scheduleTask
7682
func scheduleTask(*Task)

src/internal/task/task_asyncify.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ type state struct {
3232
panicTarget uintptr
3333
}
3434

35+
const hasReleasableStack = true
36+
3537
// stackState is the saved state of a stack while unwound.
3638
// The stack is arranged with asyncify at the bottom, C stack at the top, and a gap of available stack space between the two.
3739
type stackState struct {
@@ -70,7 +72,6 @@ func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
7072
s.entry = fn
7173
s.args = args
7274

73-
// Create a stack.
7475
stack := runtime_alloc(stackSize, nil)
7576

7677
// Set up the stack canary, a random number that should be checked when
@@ -130,6 +131,7 @@ func StopPanicUnwind(replay, target uintptr) {
130131
currentTask.state.panicReplay = replay
131132
currentTask.state.panicTarget = target
132133
}
134+
panicStackState = stackState{}
133135
}
134136

135137
func ClearPanicReplay() {
@@ -142,6 +144,7 @@ func ClearPanicReplay() {
142144
currentTask.state.panicOrigin = stackState{}
143145
currentTask.state.panicReplay = 0
144146
currentTask.state.panicTarget = 0
147+
panicStackState = stackState{}
145148
}
146149

147150
func PanicRewindData() unsafe.Pointer {
@@ -188,6 +191,12 @@ func (t *Task) Resume() {
188191
if uintptr(t.state.asyncifysp) > uintptr(t.state.csp) {
189192
runtimeFatal("stack overflow")
190193
}
194+
if t.Exited {
195+
runtime_freeTaskStack(unsafe.Pointer(t.state.canaryPtr))
196+
t.state = state{}
197+
t.gcData = gcData{}
198+
t.DeferFrame = nil
199+
}
191200
}
192201

193202
//go:linkname saveStackPointer runtime.saveStackPointer

src/internal/task/task_exit.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,16 @@ func Goexit() {
2222
exit(true)
2323
}
2424

25+
// Exit exits the current task after its entry function returns.
26+
func Exit() {
27+
exit(false)
28+
}
29+
2530
func exit(goexit bool) {
2631
t := Current()
32+
if hasReleasableStack {
33+
t.Exited = true
34+
}
2735
remaining := atomic.AddUint32(&liveTasks, ^uint32(0))
2836
if t == mainTask {
2937
if goexit {

src/internal/task/task_stack.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ type state struct {
2828
canaryPtr *uintptr
2929
}
3030

31+
const hasReleasableStack = false
32+
3133
//export tinygo_task_exit
3234
func taskExit() {
3335
exit(false)

src/runtime/gc_blocks.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,20 @@ func free(ptr unsafe.Pointer) {
537537
// TODO: free blocks on request, when the compiler knows they're unused.
538538
}
539539

540+
func freeTaskStack(ptr unsafe.Pointer) {
541+
gcLock.Lock()
542+
firstBlock := blockFromAddr(uintptr(ptr))
543+
if gcAsserts && firstBlock.pointer() != ptr {
544+
runtimeFatal("gc: freeing pointer inside allocation")
545+
}
546+
lastBlock := firstBlock.findHead()
547+
for block := firstBlock; block <= lastBlock; block++ {
548+
block.free()
549+
}
550+
insertFreeRange(ptr, uintptr(lastBlock-firstBlock+1))
551+
gcLock.Unlock()
552+
}
553+
540554
// GC performs a garbage collection cycle.
541555
func GC() {
542556
gcLock.Lock()

src/runtime/gc_boehm.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ func free(ptr unsafe.Pointer) {
102102
libgc_free(ptr)
103103
}
104104

105+
func freeTaskStack(ptr unsafe.Pointer) {
106+
free(ptr)
107+
}
108+
105109
func GC() {
106110
gcLock.Lock()
107111
libgc_gcollect()

0 commit comments

Comments
 (0)