mirror of
https://github.com/safing/portbase
synced 2025-09-11 07:44:43 +00:00
Add subsystems package and make modules able to be turned off and on
This commit is contained in:
parent
432743ccc2
commit
b4f014574b
17 changed files with 1488 additions and 391 deletions
160
modules/start.go
160
modules/start.go
|
@ -1,34 +1,38 @@
|
|||
package modules
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
var (
|
||||
startComplete = abool.NewBool(false)
|
||||
startCompleteSignal = make(chan struct{})
|
||||
initialStartCompleted = abool.NewBool(false)
|
||||
globalPrepFn func() error
|
||||
)
|
||||
|
||||
// StartCompleted returns whether starting has completed.
|
||||
func StartCompleted() bool {
|
||||
return startComplete.IsSet()
|
||||
}
|
||||
|
||||
// WaitForStartCompletion returns as soon as starting has completed.
|
||||
func WaitForStartCompletion() <-chan struct{} {
|
||||
return startCompleteSignal
|
||||
// SetGlobalPrepFn sets a global prep function that is run before all modules. This can be used to pre-initialize modules, such as setting the data root or database path.
|
||||
// SetGlobalPrepFn sets a global prep function that is run before all modules.
|
||||
func SetGlobalPrepFn(fn func() error) {
|
||||
if globalPrepFn == nil {
|
||||
globalPrepFn = fn
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts all modules in the correct order. In case of an error, it will automatically shutdown again.
|
||||
func Start() error {
|
||||
modulesLock.RLock()
|
||||
defer modulesLock.RUnlock()
|
||||
if !modulesLocked.SetToIf(false, true) {
|
||||
return errors.New("module system already started")
|
||||
}
|
||||
|
||||
// lock mgmt
|
||||
mgmtLock.Lock()
|
||||
defer mgmtLock.Unlock()
|
||||
|
||||
// start microtask scheduler
|
||||
go microTaskScheduler()
|
||||
|
@ -48,6 +52,17 @@ func Start() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// execute global prep fn
|
||||
if globalPrepFn != nil {
|
||||
err = globalPrepFn()
|
||||
if err != nil {
|
||||
if err != ErrCleanExit {
|
||||
fmt.Fprintf(os.Stderr, "CRITICAL ERROR: %s\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// prep modules
|
||||
err = prepareModules()
|
||||
if err != nil {
|
||||
|
@ -65,6 +80,9 @@ func Start() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// build dependency tree
|
||||
buildEnabledTree()
|
||||
|
||||
// start modules
|
||||
log.Info("modules: initiating...")
|
||||
err = startModules()
|
||||
|
@ -75,13 +93,11 @@ func Start() error {
|
|||
|
||||
// complete startup
|
||||
log.Infof("modules: started %d modules", len(modules))
|
||||
if startComplete.SetToIf(false, true) {
|
||||
close(startCompleteSignal)
|
||||
}
|
||||
|
||||
go taskQueueHandler()
|
||||
go taskScheduleHandler()
|
||||
|
||||
initialStartCompleted.Set()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -97,45 +113,36 @@ func prepareModules() error {
|
|||
reportCnt := 0
|
||||
|
||||
for {
|
||||
waiting := 0
|
||||
|
||||
// find modules to exec
|
||||
for _, m := range modules {
|
||||
if m.ReadyToPrep() {
|
||||
switch m.readyToPrep() {
|
||||
case statusNothingToDo:
|
||||
case statusWaiting:
|
||||
waiting++
|
||||
case statusReady:
|
||||
execCnt++
|
||||
m.inTransition.Set()
|
||||
|
||||
execM := m
|
||||
go func() {
|
||||
reports <- &report{
|
||||
module: execM,
|
||||
err: execM.runCtrlFnWithTimeout(
|
||||
"prep module",
|
||||
10*time.Second,
|
||||
execM.prep,
|
||||
),
|
||||
}
|
||||
}()
|
||||
m.prep(reports)
|
||||
}
|
||||
}
|
||||
|
||||
// check for dep loop
|
||||
if execCnt == reportCnt {
|
||||
return fmt.Errorf("modules: dependency loop detected, cannot continue")
|
||||
}
|
||||
|
||||
// wait for reports
|
||||
rep = <-reports
|
||||
rep.module.inTransition.UnSet()
|
||||
if rep.err != nil {
|
||||
if rep.err == ErrCleanExit {
|
||||
return rep.err
|
||||
if reportCnt < execCnt {
|
||||
// wait for reports
|
||||
rep = <-reports
|
||||
if rep.err != nil {
|
||||
if rep.err == ErrCleanExit {
|
||||
return rep.err
|
||||
}
|
||||
return fmt.Errorf("failed to prep module %s: %s", rep.module.Name, rep.err)
|
||||
}
|
||||
reportCnt++
|
||||
} else {
|
||||
// finished
|
||||
if waiting > 0 {
|
||||
// check for dep loop
|
||||
return fmt.Errorf("modules: dependency loop detected, cannot continue")
|
||||
}
|
||||
return fmt.Errorf("failed to prep module %s: %s", rep.module.Name, rep.err)
|
||||
}
|
||||
reportCnt++
|
||||
rep.module.Prepped.Set()
|
||||
|
||||
// exit if done
|
||||
if reportCnt == len(modules) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -149,45 +156,36 @@ func startModules() error {
|
|||
reportCnt := 0
|
||||
|
||||
for {
|
||||
waiting := 0
|
||||
|
||||
// find modules to exec
|
||||
for _, m := range modules {
|
||||
if m.ReadyToStart() {
|
||||
switch m.readyToStart() {
|
||||
case statusNothingToDo:
|
||||
case statusWaiting:
|
||||
waiting++
|
||||
case statusReady:
|
||||
execCnt++
|
||||
m.inTransition.Set()
|
||||
|
||||
execM := m
|
||||
go func() {
|
||||
reports <- &report{
|
||||
module: execM,
|
||||
err: execM.runCtrlFnWithTimeout(
|
||||
"start module",
|
||||
60*time.Second,
|
||||
execM.start,
|
||||
),
|
||||
}
|
||||
}()
|
||||
m.start(reports)
|
||||
}
|
||||
}
|
||||
|
||||
// check for dep loop
|
||||
if execCnt == reportCnt {
|
||||
return fmt.Errorf("modules: dependency loop detected, cannot continue")
|
||||
}
|
||||
|
||||
// wait for reports
|
||||
rep = <-reports
|
||||
rep.module.inTransition.UnSet()
|
||||
if rep.err != nil {
|
||||
return fmt.Errorf("modules: could not start module %s: %s", rep.module.Name, rep.err)
|
||||
}
|
||||
reportCnt++
|
||||
rep.module.Started.Set()
|
||||
log.Infof("modules: started %s", rep.module.Name)
|
||||
|
||||
// exit if done
|
||||
if reportCnt == len(modules) {
|
||||
if reportCnt < execCnt {
|
||||
// wait for reports
|
||||
rep = <-reports
|
||||
if rep.err != nil {
|
||||
return fmt.Errorf("modules: could not start module %s: %s", rep.module.Name, rep.err)
|
||||
}
|
||||
reportCnt++
|
||||
log.Infof("modules: started %s", rep.module.Name)
|
||||
} else {
|
||||
// finished
|
||||
if waiting > 0 {
|
||||
// check for dep loop
|
||||
return fmt.Errorf("modules: dependency loop detected, cannot continue")
|
||||
}
|
||||
// return last error
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue