setup signalhandler and exiter
This commit is contained in:
parent
04683e525b
commit
af58f960c5
97
exiter/exiter.go
Normal file
97
exiter/exiter.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package exiter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"gitlab.com/CRThaze/sugar/sigar"
|
||||||
|
)
|
||||||
|
|
||||||
|
const exitSignalGroupName = "exitSignalGroup"
|
||||||
|
|
||||||
|
type ExitTaskFn sigar.TaskFn
|
||||||
|
|
||||||
|
type ExiterConfig struct {
|
||||||
|
ParentCtx context.Context
|
||||||
|
ExitSignals []os.Signal
|
||||||
|
}
|
||||||
|
|
||||||
|
type Exiter struct {
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
sigar *sigar.SignalCatcher
|
||||||
|
preCancelTasks *list.List
|
||||||
|
postCancelTasks *list.List
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultExiterConfig = ExiterConfig{
|
||||||
|
ParentCtx: context.Background(),
|
||||||
|
ExitSignals: []os.Signal{
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
syscall.SIGABRT,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExiterWithConfig(config ExiterConfig) (e *Exiter) {
|
||||||
|
e = &Exiter{}
|
||||||
|
e.ctx, e.cancel = context.WithCancel(config.ParentCtx)
|
||||||
|
e.sigar = sigar.NewSignalCatcher(
|
||||||
|
sigar.SignalGroups{
|
||||||
|
exitSignalGroupName: config.ExitSignals,
|
||||||
|
},
|
||||||
|
e.ctx,
|
||||||
|
)
|
||||||
|
e.sigar.RegisterTask(exitSignalGroupName, func() {
|
||||||
|
e.Exit(0)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExiter() *Exiter { return NewExiterWithConfig(DefaultExiterConfig) }
|
||||||
|
|
||||||
|
func SpawnExiter() (e *Exiter) {
|
||||||
|
e = NewExiter()
|
||||||
|
e.ListenForSignals()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Exiter) ListenForSignals() {
|
||||||
|
e.sigar.Listen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Exiter) RegisterPreTask(fn ExitTaskFn) {
|
||||||
|
e.preCancelTasks.PushBack(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Exiter) RegisterPostTask(fn ExitTaskFn) {
|
||||||
|
e.postCancelTasks.PushBack(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Exiter) Exit(code ...int) {
|
||||||
|
for x := e.preCancelTasks.Front(); x != nil; x = x.Next() {
|
||||||
|
x.Value.(ExitTaskFn)()
|
||||||
|
}
|
||||||
|
e.cancel()
|
||||||
|
for x := e.postCancelTasks.Front(); x != nil; x = x.Next() {
|
||||||
|
x.Value.(ExitTaskFn)()
|
||||||
|
}
|
||||||
|
if len(code) == 0 {
|
||||||
|
code = make([]int, 1, 1)
|
||||||
|
}
|
||||||
|
os.Exit(code[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Exiter) Wait() {
|
||||||
|
<-e.ctx.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Exiter) GetCancelableContext() context.Context {
|
||||||
|
return e.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Exiter) GetSignalCatcher() *sigar.SignalCatcher {
|
||||||
|
return e.sigar
|
||||||
|
}
|
87
sigar/signalHandler.go
Normal file
87
sigar/signalHandler.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package sigar
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type taskMap map[string]*list.List
|
||||||
|
|
||||||
|
type SignalGroups map[string][]os.Signal
|
||||||
|
|
||||||
|
func (sg SignalGroups) initTaskMap(tm taskMap) {
|
||||||
|
for groupName := range sg {
|
||||||
|
tm[groupName] = list.New()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaskFn func()
|
||||||
|
|
||||||
|
type SignalCatcher struct {
|
||||||
|
signals SignalGroups
|
||||||
|
tasks map[string]*list.List
|
||||||
|
exitCtx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSignalCatcher(sg SignalGroups, exitCtx context.Context) (sc *SignalCatcher) {
|
||||||
|
if sg == nil {
|
||||||
|
sg = SignalGroups{}
|
||||||
|
}
|
||||||
|
sc = &SignalCatcher{
|
||||||
|
signals: sg,
|
||||||
|
exitCtx: exitCtx,
|
||||||
|
tasks: taskMap{},
|
||||||
|
}
|
||||||
|
sg.initTaskMap(sc.tasks)
|
||||||
|
if exitCtx == nil {
|
||||||
|
// Create a cancelable context and discard the cancel function, as we will never use it.
|
||||||
|
sc.exitCtx, _ = context.WithCancel(context.Background())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func SpawnSignalCatcher(sg SignalGroups, exitCtx context.Context) (sc *SignalCatcher) {
|
||||||
|
sc = NewSignalCatcher(sg, exitCtx)
|
||||||
|
sc.Listen()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SignalCatcher) runTasks(group string) {
|
||||||
|
taskList := sc.tasks[group]
|
||||||
|
for e := taskList.Front(); e != nil; e = e.Next() {
|
||||||
|
e.Value.(TaskFn)()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SignalCatcher) Listen() {
|
||||||
|
for groupName, signalSet := range sc.signals {
|
||||||
|
// Prevent loop closure by copying variables.
|
||||||
|
group := groupName
|
||||||
|
signals := signalSet
|
||||||
|
go func() {
|
||||||
|
sigChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigChan, signals...)
|
||||||
|
select {
|
||||||
|
case <-sigChan:
|
||||||
|
sc.runTasks(group)
|
||||||
|
case <-sc.exitCtx.Done():
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SignalCatcher) RegisterTaskGroup(group string, signals []os.Signal) {
|
||||||
|
sc.signals[group] = signals
|
||||||
|
sc.tasks[group] = list.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *SignalCatcher) RegisterTask(group string, fn TaskFn) error {
|
||||||
|
if _, ok := sc.tasks[group]; !ok {
|
||||||
|
return errors.New("No such task group")
|
||||||
|
}
|
||||||
|
sc.tasks[group].PushBack(fn)
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user