mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-23 05:49:01 +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 datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
target triple = "thumbv7m-unknown-unknown-eabi"
|
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._interface = type { ptr, ptr }
|
||||||
%runtime._defer = type { i32, ptr }
|
%runtime._defer = type { i32, ptr }
|
||||||
|
|
||||||
|
|||||||
+29
-7
@@ -38,12 +38,24 @@ type deferFrame struct {
|
|||||||
JumpPC unsafe.Pointer // pc to return to
|
JumpPC unsafe.Pointer // pc to return to
|
||||||
ExtraRegs [deferExtraRegs]unsafe.Pointer // extra registers (depending on the architecture)
|
ExtraRegs [deferExtraRegs]unsafe.Pointer // extra registers (depending on the architecture)
|
||||||
Previous *deferFrame // previous recover buffer pointer
|
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
|
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.
|
// Builtin function panic(msg), used as a compiler intrinsic.
|
||||||
func _panic(message interface{}) {
|
func _panic(message interface{}) {
|
||||||
|
panicOrGoexit(message, panicTrue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func panicOrGoexit(message interface{}, panicking panicState) {
|
||||||
if panicStrategy() == tinygo.PanicStrategyTrap {
|
if panicStrategy() == tinygo.PanicStrategyTrap {
|
||||||
trap()
|
trap()
|
||||||
}
|
}
|
||||||
@@ -53,11 +65,16 @@ func _panic(message interface{}) {
|
|||||||
frame := (*deferFrame)(task.Current().DeferFrame)
|
frame := (*deferFrame)(task.Current().DeferFrame)
|
||||||
if frame != nil {
|
if frame != nil {
|
||||||
frame.PanicValue = message
|
frame.PanicValue = message
|
||||||
frame.Panicking = true
|
frame.Panicking = panicking
|
||||||
tinygo_longjmp(frame)
|
tinygo_longjmp(frame)
|
||||||
// unreachable
|
// unreachable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if panicking == panicGoexit {
|
||||||
|
// Call to Goexit() instead of a panic.
|
||||||
|
// Exit the goroutine instead of printing a panic message.
|
||||||
|
deadlock()
|
||||||
|
}
|
||||||
printstring("panic: ")
|
printstring("panic: ")
|
||||||
printitf(message)
|
printitf(message)
|
||||||
printnl()
|
printnl()
|
||||||
@@ -103,7 +120,7 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
|
|||||||
currentTask := task.Current()
|
currentTask := task.Current()
|
||||||
frame.Previous = (*deferFrame)(currentTask.DeferFrame)
|
frame.Previous = (*deferFrame)(currentTask.DeferFrame)
|
||||||
frame.JumpSP = jumpSP
|
frame.JumpSP = jumpSP
|
||||||
frame.Panicking = false
|
frame.Panicking = panicFalse
|
||||||
currentTask.DeferFrame = unsafe.Pointer(frame)
|
currentTask.DeferFrame = unsafe.Pointer(frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,10 +132,10 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
|
|||||||
//go:nobounds
|
//go:nobounds
|
||||||
func destroyDeferFrame(frame *deferFrame) {
|
func destroyDeferFrame(frame *deferFrame) {
|
||||||
task.Current().DeferFrame = unsafe.Pointer(frame.Previous)
|
task.Current().DeferFrame = unsafe.Pointer(frame.Previous)
|
||||||
if frame.Panicking {
|
if frame.Panicking != panicFalse {
|
||||||
// We're still panicking!
|
// We're still panicking!
|
||||||
// Re-raise the panic now.
|
// 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.
|
// already), but instead from the previous frame.
|
||||||
frame = frame.Previous
|
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
|
// Only the first call to recover returns the panic value. It also stops
|
||||||
// the panicking sequence, hence setting panicking to false.
|
// the panicking sequence, hence setting panicking to false.
|
||||||
frame.Panicking = false
|
frame.Panicking = panicFalse
|
||||||
return frame.PanicValue
|
return frame.PanicValue
|
||||||
}
|
}
|
||||||
// Not panicking, so return a nil interface.
|
// 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.
|
// 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() {
|
func Goexit() {
|
||||||
// TODO: run deferred functions
|
panicOrGoexit(nil, panicGoexit)
|
||||||
deadlock()
|
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+19
@@ -1,5 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println("# simple recover")
|
println("# simple recover")
|
||||||
recoverSimple()
|
recoverSimple()
|
||||||
@@ -22,6 +27,9 @@ func main() {
|
|||||||
|
|
||||||
println("\n# defer panic")
|
println("\n# defer panic")
|
||||||
deferPanic()
|
deferPanic()
|
||||||
|
|
||||||
|
println("\n# runtime.Goexit")
|
||||||
|
runtimeGoexit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func recoverSimple() {
|
func recoverSimple() {
|
||||||
@@ -104,6 +112,17 @@ func deferPanic() {
|
|||||||
println("defer panic")
|
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{}) {
|
func printitf(msg string, itf interface{}) {
|
||||||
switch itf := itf.(type) {
|
switch itf := itf.(type) {
|
||||||
case string:
|
case string:
|
||||||
|
|||||||
Vendored
+3
@@ -27,3 +27,6 @@ recovered: panic 2
|
|||||||
# defer panic
|
# defer panic
|
||||||
defer panic
|
defer panic
|
||||||
recovered from deferred call: deferred panic
|
recovered from deferred call: deferred panic
|
||||||
|
|
||||||
|
# runtime.Goexit
|
||||||
|
Goexit deferred function, recover is nil: true
|
||||||
|
|||||||
Reference in New Issue
Block a user