1616//
1717// // fn is the actual main function
1818// func fn() {
19- // // ... do whatever you want to do ...
19+ // // ... do stuff ...
2020//
21- // // mainthread.Call returns when f1 returns. Note that if f1
22- // // blocks it will also block the execution of any subsequent
23- // // calls on the main thread.
21+ // // mainthread.Call returns when f1 returns. Note that if f1 blocks
22+ // // it will also block the execution of any subsequent calls on the
23+ // // main thread.
2424// mainthread.Call(f1)
2525//
26- // // ... do whatever you want to do ...
26+ // // ... do stuff ...
27+ //
2728//
2829// // mainthread.Go returns immediately and f2 is scheduled to be
2930// // executed in the future.
3031// mainthread.Go(f2)
3132//
32- // // ... do whatever you want to do ...
33+ // // ... do stuff ...
3334// }
3435//
3536// func f1() { ... }
3637// func f2() { ... }
38+ //
39+ // If the given function triggers a panic, and called via `mainthread.Call`,
40+ // then the panic will be propagated to the same goroutine. One can capture
41+ // that panic, when possible:
42+ //
43+ // defer func() {
44+ // if r := recover(); r != nil {
45+ // println(r)
46+ // }
47+ // }()
48+ //
49+ // mainthread.Call(func() { ... }) // if panic
50+ //
51+ // If the given function triggers a panic, and called via `mainthread.Go`,
52+ // then the panic will be cached internally, until a call to the `Error()` method:
53+ //
54+ // mainthread.Go(func() { ... }) // if panics
55+ //
56+ // // ... do stuff ...
57+ //
58+ // if err := mainthread.Error(); err != nil { // can be captured here.
59+ // println(err)
60+ // }
61+ //
62+ // Note that a panic happens before `mainthread.Error()` returning the
63+ // panicked error. If one needs to guarantee `mainthread.Error()` indeed
64+ // captured the panic, a dummy function can be used as synchornization:
65+ //
66+ // mainthread.Go(func() { panic("die") }) // if panics
67+ // mainthread.Call(func() {}) // for execution synchronization
68+ // err := mainthread.Error() // err must be non-nil
69+ //
70+ // It is possible to cache up to a maximum of 42 panicked errors.
71+ // More errors are ignored.
3772package mainthread // import "golang.design/x/mainthread"
3873
3974import (
75+ "fmt"
4076 "runtime"
4177 "sync"
4278)
@@ -50,23 +86,39 @@ func init() {
5086//
5187// Init must be called in the main.main function.
5288func Init (main func ()) {
53- done := donePool .Get ().(chan struct {} )
89+ done := donePool .Get ().(chan error )
5490 defer donePool .Put (done )
5591
5692 go func () {
5793 defer func () {
58- done <- struct {}{}
94+ done <- nil
5995 }()
6096 main ()
6197 }()
6298
6399 for {
64100 select {
65101 case f := <- funcQ :
66- f .fn ()
67- if f .done != nil {
68- f .done <- struct {}{}
69- }
102+ func () {
103+ defer func () {
104+ r := recover ()
105+ if f .done != nil {
106+ if r != nil {
107+ f .done <- fmt .Errorf ("%v" , r )
108+ } else {
109+ f .done <- nil
110+ }
111+ } else {
112+ if r != nil {
113+ select {
114+ case erroQ <- fmt .Errorf ("%v" , r ):
115+ default :
116+ }
117+ }
118+ }
119+ }()
120+ f .fn ()
121+ }()
70122 case <- done :
71123 return
72124 }
@@ -75,26 +127,44 @@ func Init(main func()) {
75127
76128// Call calls f on the main thread and blocks until f finishes.
77129func Call (f func ()) {
78- done := donePool .Get ().(chan struct {} )
130+ done := donePool .Get ().(chan error )
79131 defer donePool .Put (done )
80132
81- funcQ <- funcData {fn : f , done : done }
82- <- done
133+ data := funcData {fn : f , done : done }
134+ funcQ <- data
135+ if err := <- done ; err != nil {
136+ panic (err )
137+ }
83138}
84139
85140// Go schedules f to be called on the main thread.
86141func Go (f func ()) {
87142 funcQ <- funcData {fn : f }
88143}
89144
145+ // Error returns an error that is captured if there are any panics
146+ // happened on the mainthread.
147+ //
148+ // It is possible to cache up to a maximum of 42 panicked errors.
149+ // More errors are ignored.
150+ func Error () error {
151+ select {
152+ case err := <- erroQ :
153+ return err
154+ default :
155+ return nil
156+ }
157+ }
158+
90159var (
91160 funcQ = make (chan funcData , runtime .GOMAXPROCS (0 ))
161+ erroQ = make (chan error , 42 )
92162 donePool = sync.Pool {New : func () interface {} {
93- return make (chan struct {} )
163+ return make (chan error )
94164 }}
95165)
96166
97167type funcData struct {
98168 fn func ()
99- done chan struct {}
169+ done chan error
100170}
0 commit comments