Skip to content

Commit aed555d

Browse files
aykevldeadprogram
authored andcommitted
runtime: implement Goexit
This is needed for full support for the testing package
1 parent 65b085a commit aed555d

File tree

5 files changed

+53
-14
lines changed

5 files changed

+53
-14
lines changed

compiler/testdata/defer-cortex-m-qemu.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ source_filename = "defer.go"
33
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
44
target triple = "thumbv7m-unknown-unknown-eabi"
55

6-
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface }
6+
%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i8, %runtime._interface }
77
%runtime._interface = type { ptr, ptr }
88
%runtime._defer = type { i32, ptr }
99

src/runtime/panic.go

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,24 @@ type deferFrame struct {
3838
JumpPC unsafe.Pointer // pc to return to
3939
ExtraRegs [deferExtraRegs]unsafe.Pointer // extra registers (depending on the architecture)
4040
Previous *deferFrame // previous recover buffer pointer
41-
Panicking bool // true iff this defer frame is panicking
41+
Panicking panicState // not panicking, panicking, or in Goexit
4242
PanicValue interface{} // panic value, might be nil for panic(nil) for example
4343
}
4444

45+
type panicState uint8
46+
47+
const (
48+
panicFalse panicState = iota
49+
panicTrue
50+
panicGoexit
51+
)
52+
4553
// Builtin function panic(msg), used as a compiler intrinsic.
4654
func _panic(message interface{}) {
55+
panicOrGoexit(message, panicTrue)
56+
}
57+
58+
func panicOrGoexit(message interface{}, panicking panicState) {
4759
if panicStrategy() == tinygo.PanicStrategyTrap {
4860
trap()
4961
}
@@ -53,11 +65,16 @@ func _panic(message interface{}) {
5365
frame := (*deferFrame)(task.Current().DeferFrame)
5466
if frame != nil {
5567
frame.PanicValue = message
56-
frame.Panicking = true
68+
frame.Panicking = panicking
5769
tinygo_longjmp(frame)
5870
// unreachable
5971
}
6072
}
73+
if panicking == panicGoexit {
74+
// Call to Goexit() instead of a panic.
75+
// Exit the goroutine instead of printing a panic message.
76+
deadlock()
77+
}
6178
printstring("panic: ")
6279
printitf(message)
6380
printnl()
@@ -103,7 +120,7 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
103120
currentTask := task.Current()
104121
frame.Previous = (*deferFrame)(currentTask.DeferFrame)
105122
frame.JumpSP = jumpSP
106-
frame.Panicking = false
123+
frame.Panicking = panicFalse
107124
currentTask.DeferFrame = unsafe.Pointer(frame)
108125
}
109126

@@ -115,10 +132,10 @@ func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) {
115132
//go:nobounds
116133
func destroyDeferFrame(frame *deferFrame) {
117134
task.Current().DeferFrame = unsafe.Pointer(frame.Previous)
118-
if frame.Panicking {
135+
if frame.Panicking != panicFalse {
119136
// We're still panicking!
120137
// Re-raise the panic now.
121-
_panic(frame.PanicValue)
138+
panicOrGoexit(frame.PanicValue, frame.Panicking)
122139
}
123140
}
124141

@@ -143,10 +160,15 @@ func _recover(useParentFrame bool) interface{} {
143160
// already), but instead from the previous frame.
144161
frame = frame.Previous
145162
}
146-
if frame != nil && frame.Panicking {
163+
if frame != nil && frame.Panicking != panicFalse {
164+
if frame.Panicking == panicGoexit {
165+
// Special value that indicates we're exiting the goroutine using
166+
// Goexit(). Therefore, make this recover call a no-op.
167+
return nil
168+
}
147169
// Only the first call to recover returns the panic value. It also stops
148170
// the panicking sequence, hence setting panicking to false.
149-
frame.Panicking = false
171+
frame.Panicking = panicFalse
150172
return frame.PanicValue
151173
}
152174
// Not panicking, so return a nil interface.

src/runtime/scheduler.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@ func scheduleLogChan(msg string, ch *channel, t *task.Task) {
2828
}
2929

3030
// Goexit terminates the currently running goroutine. No other goroutines are affected.
31-
//
32-
// Unlike the main Go implementation, no deferred calls will be run.
33-
//
34-
//go:inline
3531
func Goexit() {
36-
// TODO: run deferred functions
37-
deadlock()
32+
panicOrGoexit(nil, panicGoexit)
3833
}

testdata/recover.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package main
22

3+
import (
4+
"runtime"
5+
"time"
6+
)
7+
38
func main() {
49
println("# simple recover")
510
recoverSimple()
@@ -22,6 +27,9 @@ func main() {
2227

2328
println("\n# defer panic")
2429
deferPanic()
30+
31+
println("\n# runtime.Goexit")
32+
runtimeGoexit()
2533
}
2634

2735
func recoverSimple() {
@@ -104,6 +112,17 @@ func deferPanic() {
104112
println("defer panic")
105113
}
106114

115+
func runtimeGoexit() {
116+
go func() {
117+
defer func() {
118+
println("Goexit deferred function, recover is nil:", recover() == nil)
119+
}()
120+
121+
runtime.Goexit()
122+
}()
123+
time.Sleep(time.Millisecond)
124+
}
125+
107126
func printitf(msg string, itf interface{}) {
108127
switch itf := itf.(type) {
109128
case string:

testdata/recover.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ recovered: panic 2
2727
# defer panic
2828
defer panic
2929
recovered from deferred call: deferred panic
30+
31+
# runtime.Goexit
32+
Goexit deferred function, recover is nil: true

0 commit comments

Comments
 (0)