runner
runner copied to clipboard
thoughts on supporting functions that return errors and handling pancis
What are your thoughts on adding support for getting errors back and thereby also handling panics, e.g.,
// RunErr runs a function of type func() error in a new goroutine.
// The returned channel is closed when f returns.
func (r *Runner) RunErr(f func() error) <-chan error {
done := make(chan error, 1)
r.wg.Add(1)
go func() {
var ret error
defer func() {
rec := recover()
if recErr, ok := rec.(error); ok {
ret = recErr
} else if rec != nil {
ret = errors.New(fmt.Sprintf("panic: %+v", rec))
}
done <- ret
close(done)
r.wg.Done()
}()
ret = f()
}()
return done
}
(and also RunErrContext and RunErrOtherContext)
And a bit related, what about a Done() <-chan struct{}:
func (r *Runner) Done() <-chan struct{} {
done := make(chan struct{})
go func() {
r.Wait()
close(done)
}()
return done
}
The reason I'm asking is because then you can do things like:
func main() {
ctx, cancel := context.WithCancel(context.Background())
r := runner.Runner{Ctx: ctx}
httpTask := r.RunErrContext(runHTTP)
tcpTask := r.RunErrContext(runTCP)
signalTask := r.RunSigs(syscall.SIGINT, syscall.SIGTERM)
Loop:
for {
select {
case <-signalTask:
// setting channel to nil so it won't fire again next round
signalTask = nil
cancel()
case err := <-httpTask:
// if http errors, stop everything
httpTask = nil
if err != nil {
cancel()
}
case <-tcpTask:
// don't care about tcp error
tcpTask = nil
case <-r.Done():
// when wg is done, all goroutines have finished
fmt.Println("All done")
break Loop
}
}
}
(assuming wg.Done() is called in goroutine after close(done)