Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/testdata/defer-cortex-m-qemu.ll
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ source_filename = "defer.go"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7m-unknown-unknown-eabi"

%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface }
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface }
%runtime._interface = type { ptr, ptr }
%runtime._defer = type { i32, ptr }

Expand Down
36 changes: 29 additions & 7 deletions src/runtime/panic.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,24 @@ type deferFrame struct {
JumpPC unsafe.Pointer // pc to return to
ExtraRegs [deferExtraRegs]unsafe.Pointer // extra registers (depending on the architecture)
Previous *deferFrame // previous recover buffer pointer
Panicking bool // true iff this defer frame is panicking
Panicking panicState // not panicking, panicking, or in Goexit
PanicValue interface{} // panic value, might be nil for panic(nil) for example
}

type panicState uint8

const (
panicFalse panicState = iota
panicTrue
panicGoexit
)

// Builtin function panic(msg), used as a compiler intrinsic.
func _panic(message interface{}) {
panicOrGoexit(message, panicTrue)
}

func panicOrGoexit(message interface{}, panicking panicState) {
if panicStrategy() == tinygo.PanicStrategyTrap {
trap()
}
Expand All @@ -53,11 +65,16 @@ func _panic(message interface{}) {
frame := (*deferFrame)(task.Current().DeferFrame)
if frame != nil {
frame.PanicValue = message
frame.Panicking = true
frame.Panicking = panicking
tinygo_longjmp(frame)
// unreachable
}
}
if panicking == panicGoexit {
// Call to Goexit() instead of a panic.
// Exit the goroutine instead of printing a panic message.
deadlock()
}
printstring("panic: ")
printitf(message)
printnl()
Expand Down Expand Up @@ -103,7 +120,7 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
currentTask := task.Current()
frame.Previous = (*deferFrame)(currentTask.DeferFrame)
frame.JumpSP = jumpSP
frame.Panicking = false
frame.Panicking = panicFalse
currentTask.DeferFrame = unsafe.Pointer(frame)
}

Expand All @@ -115,10 +132,10 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
//go:nobounds
func destroyDeferFrame(frame *deferFrame) {
task.Current().DeferFrame = unsafe.Pointer(frame.Previous)
if frame.Panicking {
if frame.Panicking != panicFalse {
// We're still panicking!
// Re-raise the panic now.
_panic(frame.PanicValue)
panicOrGoexit(frame.PanicValue, frame.Panicking)
}
}

Expand All @@ -143,10 +160,15 @@ func _recover(useParentFrame bool) interface{} {
// already), but instead from the previous frame.
frame = frame.Previous
}
if frame != nil && frame.Panicking {
if frame != nil && frame.Panicking != panicFalse {
if frame.Panicking == panicGoexit {
// Special value that indicates we're exiting the goroutine using
// Goexit(). Therefore, make this recover call a no-op.
return nil
}
// Only the first call to recover returns the panic value. It also stops
// the panicking sequence, hence setting panicking to false.
frame.Panicking = false
frame.Panicking = panicFalse
return frame.PanicValue
}
// Not panicking, so return a nil interface.
Expand Down
7 changes: 1 addition & 6 deletions src/runtime/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ func scheduleLogChan(msg string, ch *channel, t *task.Task) {
}

// Goexit terminates the currently running goroutine. No other goroutines are affected.
//
// Unlike the main Go implementation, no deferred calls will be run.
//
//go:inline
func Goexit() {
// TODO: run deferred functions
deadlock()
panicOrGoexit(nil, panicGoexit)
}
19 changes: 19 additions & 0 deletions testdata/recover.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package main

import (
"runtime"
"time"
)

func main() {
println("# simple recover")
recoverSimple()
Expand All @@ -22,6 +27,9 @@ func main() {

println("\n# defer panic")
deferPanic()

println("\n# runtime.Goexit")
runtimeGoexit()
}

func recoverSimple() {
Expand Down Expand Up @@ -104,6 +112,17 @@ func deferPanic() {
println("defer panic")
}

func runtimeGoexit() {
go func() {
defer func() {
println("Goexit deferred function, recover is nil:", recover() == nil)
}()

runtime.Goexit()
}()
time.Sleep(time.Millisecond)
}

func printitf(msg string, itf interface{}) {
switch itf := itf.(type) {
case string:
Expand Down
3 changes: 3 additions & 0 deletions testdata/recover.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ recovered: panic 2
# defer panic
defer panic
recovered from deferred call: deferred panic

# runtime.Goexit
Goexit deferred function, recover is nil: true