aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/runtime
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2024-11-21 09:20:14 +0100
committerRon Evans <[email protected]>2024-11-30 11:55:22 +0100
commitaed555d858c163fd96c12db3d5ec376cc5261e73 (patch)
tree42248e37aac247da9b6c8f2df4d3421e61f1a3d5 /src/runtime
parent65b085a5d5abbbfc3c9ed6fa3461c5032f45e281 (diff)
downloadtinygo-aed555d858c163fd96c12db3d5ec376cc5261e73.tar.gz
tinygo-aed555d858c163fd96c12db3d5ec376cc5261e73.zip
runtime: implement Goexit
This is needed for full support for the testing package
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/panic.go36
-rw-r--r--src/runtime/scheduler.go7
2 files changed, 30 insertions, 13 deletions
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index d429171ec..ec33a4469 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -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.
diff --git a/src/runtime/scheduler.go b/src/runtime/scheduler.go
index d84ccf3e0..727c7f5f2 100644
--- a/src/runtime/scheduler.go
+++ b/src/runtime/scheduler.go
@@ -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)
}