Routine manager can now be shut down

This commit is contained in:
Sasha Koshka 2023-05-31 18:07:38 -04:00
parent 066247a08f
commit 631796a726

View File

@ -7,9 +7,43 @@ import "log"
import "time" import "time"
import "sync" import "sync"
// Routine is a long-running function that does not return until it is finished. type routine struct {
// An error is returned if the routine exited due to an error. run, shutdown func () error
type Routine func () error }
func (routine routine) Run () error {
if routine.run == nil {
return nil
} else {
return routine.run()
}
}
func (routine routine) Shutdown () error {
if routine.shutdown == nil {
return nil
} else {
return routine.shutdown()
}
}
// From creates a routine from a separate run and shutdown function.
func From (run, shutdown func () error) Routine {
return routine {
run: run,
shutdown: shutdown,
}
}
// Routine is an object that can be run and stopped.
type Routine interface {
// Run is a long-running function that does not return until it is
// finished. An error is returned if the routine exited due to an error.
Run () error
// Shutdown stops Run.
Shutdown () error
}
// Manager is a system capable of managing multiple routines, and restarting // Manager is a system capable of managing multiple routines, and restarting
// them if they fail. // them if they fail.
@ -27,6 +61,9 @@ type Manager struct {
// is nil, messages will be written to the standard logger. To disable // is nil, messages will be written to the standard logger. To disable
// logging altogether, this can be set to io.Discard. // logging altogether, this can be set to io.Discard.
Logger io.Writer Logger io.Writer
stoppingMutex sync.Mutex
stopping bool
} }
// Run spawns all routines in the Routines slice. If a routine exits with an // Run spawns all routines in the Routines slice. If a routine exits with an
@ -47,6 +84,21 @@ func (manager *Manager) Run () error {
return errExit return errExit
} }
// Shutdown shuts down all routines in the manager.
func (manager *Manager) Shutdown () (err error) {
manager.stoppingMutex.Lock()
manager.stopping = true
manager.stoppingMutex.Unlock()
for _, routine := range manager.Routines {
routineErr := routine.Shutdown()
if routineErr != nil {
err = routineErr
}
}
return
}
// Append adds one or more routines to the Routines slice. This has no effect if // Append adds one or more routines to the Routines slice. This has no effect if
// the manager is already running. // the manager is already running.
func (manager *Manager) Append (routines ...Routine) { func (manager *Manager) Append (routines ...Routine) {
@ -66,9 +118,15 @@ func (manager *Manager) runRoutine (routine Routine, group *sync.WaitGroup, errE
var err error var err error
for { for {
stopping := false
manager.stoppingMutex.Lock()
stopping = manager.stopping
manager.stoppingMutex.Unlock()
if stopping { break }
// TODO: recover from panics // TODO: recover from panics
lastStart := time.Now() lastStart := time.Now()
err = routine() err = routine.Run()
if err == nil { if err == nil {
manager.log("(i) routine exited") manager.log("(i) routine exited")