From 92b645f34cbab28dbfd32699d40afe8b665706cd Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Wed, 31 May 2023 18:49:00 -0400 Subject: [PATCH] Routine manager now recovers from panicking goroutines --- routines/routines.go | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/routines/routines.go b/routines/routines.go index f727bb2..9d9431a 100644 --- a/routines/routines.go +++ b/routines/routines.go @@ -6,6 +6,7 @@ import "fmt" import "log" import "time" import "sync" +import "errors" type routine struct { run, shutdown func () error @@ -71,17 +72,16 @@ type Manager struct { // Run returns only when all routines have exited. func (manager *Manager) Run () error { var waitGroup sync.WaitGroup - var errExit error for _, routine := range manager.Routines { if routine != nil { waitGroup.Add(1) - go manager.runRoutine(routine, &waitGroup, &errExit) + go manager.runRoutine(routine, &waitGroup) } } waitGroup.Wait() - return errExit + return nil } // Shutdown shuts down all routines in the manager. @@ -113,21 +113,26 @@ func (manager *Manager) log (message ...any) { } } -func (manager *Manager) runRoutine (routine Routine, group *sync.WaitGroup, errExit *error) { +func (manager *Manager) runRoutine (routine Routine, group *sync.WaitGroup) { defer group.Done() - var err error for { + lastStart := time.Now() + err := panicWrap(routine.Run) + stopping := false manager.stoppingMutex.Lock() stopping = manager.stopping manager.stoppingMutex.Unlock() - if stopping { break } - - // TODO: recover from panics - lastStart := time.Now() - err = routine.Run() - + if stopping { + if err == nil { + manager.log("(i) stopped routine") + } else { + manager.log("!!! stopped routine, with error:", err) + } + break + } + if err == nil { manager.log("(i) routine exited") break @@ -142,8 +147,15 @@ func (manager *Manager) runRoutine (routine Routine, group *sync.WaitGroup, errE manager.log("(i) routine is being restarted") } } - - if err != nil { - *errExit = err - } +} + +func panicWrap (f func () error) (err error) { + defer func () { + if pan := recover(); pan != nil { + err = errors.New(fmt.Sprint(pan)) + } + } () + + err = f() + return }