mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
runtime: implement Goexit
This is needed for full support for the testing package
This commit is contained in:
committed by
Ron Evans
parent
65b085a5d5
commit
aed555d858
+1
-1
@@ -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 }
|
||||
|
||||
|
||||
+29
-7
@@ -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()
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Vendored
+19
@@ -1,5 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
println("# simple recover")
|
||||
recoverSimple()
|
||||
@@ -22,6 +27,9 @@ func main() {
|
||||
|
||||
println("\n# defer panic")
|
||||
deferPanic()
|
||||
|
||||
println("\n# runtime.Goexit")
|
||||
runtimeGoexit()
|
||||
}
|
||||
|
||||
func recoverSimple() {
|
||||
@@ -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:
|
||||
|
||||
Vendored
+3
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user