safing-portbase/modules/stop.go
2018-12-12 19:18:49 +01:00

102 lines
2 KiB
Go

package modules
import (
"fmt"
"github.com/tevino/abool"
"github.com/Safing/portbase/log"
)
var (
shutdownSignal = make(chan struct{})
shutdownSignalClosed = abool.NewBool(false)
)
// ShuttingDown returns a channel read on the global shutdown signal.
func ShuttingDown() <-chan struct{} {
return shutdownSignal
}
func checkStopStatus() (readyToStop []*Module, done bool) {
active := 0
// collect all active modules
activeModules := make(map[string]*Module)
for _, module := range modules {
if module.Active.IsSet() {
active++
activeModules[module.Name] = module
}
}
if active == 0 {
return nil, true
}
// remove modules that others depend on
for _, module := range activeModules {
for _, depName := range module.dependencies {
delete(activeModules, depName)
}
}
// make list out of map, minus modules in transition
for _, module := range activeModules {
if !module.inTransition.IsSet() {
readyToStop = append(readyToStop, module)
}
}
return readyToStop, false
}
// Shutdown stops all modules in the correct order.
func Shutdown() error {
if startComplete.IsSet() {
log.Warning("modules: starting shutdown...")
modulesLock.Lock()
defer modulesLock.Unlock()
} else {
log.Warning("modules: aborting, shutting down...")
}
if shutdownSignalClosed.SetToIf(false, true) {
close(shutdownSignal)
}
reports := make(chan error, 10)
for {
readyToStop, done := checkStopStatus()
if done {
break
}
for _, module := range readyToStop {
module.inTransition.Set()
nextModule := module // workaround go vet alert
go func() {
err := nextModule.stop()
if err != nil {
reports <- fmt.Errorf("modules: could not stop module %s: %s", nextModule.Name, err)
} else {
reports <- nil
}
nextModule.Active.UnSet()
nextModule.inTransition.UnSet()
}()
}
err := <-reports
if err != nil {
log.Error(err.Error())
return err
}
}
log.Info("modules: shutdown complete")
log.Shutdown()
return nil
}