mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-10-19 15:54:24 +00:00
176 lines
3.7 KiB
Go
176 lines
3.7 KiB
Go
package adapter
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/sagernet/sing-box/common/taskmonitor"
|
|
C "github.com/sagernet/sing-box/constant"
|
|
"github.com/sagernet/sing-box/log"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
F "github.com/sagernet/sing/common/format"
|
|
"github.com/sagernet/sing/common/x/list"
|
|
)
|
|
|
|
type SimpleLifecycle interface {
|
|
Start() error
|
|
Close() error
|
|
}
|
|
|
|
type StartStage uint8
|
|
|
|
const (
|
|
StartStateInitialize StartStage = iota
|
|
StartStateStart
|
|
StartStatePostStart
|
|
StartStateStarted
|
|
)
|
|
|
|
var ListStartStages = []StartStage{
|
|
StartStateInitialize,
|
|
StartStateStart,
|
|
StartStatePostStart,
|
|
StartStateStarted,
|
|
}
|
|
|
|
func (s StartStage) String() string {
|
|
switch s {
|
|
case StartStateInitialize:
|
|
return "initialize"
|
|
case StartStateStart:
|
|
return "start"
|
|
case StartStatePostStart:
|
|
return "post-start"
|
|
case StartStateStarted:
|
|
return "finish-start"
|
|
default:
|
|
panic("unknown stage")
|
|
}
|
|
}
|
|
|
|
type Lifecycle interface {
|
|
Start(stage StartStage, scope *Scope) error
|
|
}
|
|
|
|
type LifecycleService interface {
|
|
Name() string
|
|
Lifecycle
|
|
}
|
|
|
|
type Scope struct {
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
logger log.ContextLogger
|
|
access sync.Mutex
|
|
cleanups list.List[func() error]
|
|
children map[Lifecycle]*scopeChild
|
|
}
|
|
|
|
type scopeChild struct {
|
|
scope *Scope
|
|
cleanup *list.Element[func() error]
|
|
}
|
|
|
|
func NewScope(ctx context.Context, logger log.ContextLogger) *Scope {
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
return &Scope{
|
|
ctx: ctx,
|
|
cancel: cancel,
|
|
logger: logger,
|
|
children: make(map[Lifecycle]*scopeChild),
|
|
}
|
|
}
|
|
|
|
func (s *Scope) Context() context.Context {
|
|
return s.ctx
|
|
}
|
|
|
|
func (s *Scope) Add(cleanup func() error) {
|
|
s.access.Lock()
|
|
s.cleanups.PushBack(cleanup)
|
|
s.access.Unlock()
|
|
}
|
|
|
|
func (s *Scope) Start(name string, component Lifecycle, stage StartStage) error {
|
|
s.access.Lock()
|
|
err := s.ctx.Err()
|
|
if err != nil {
|
|
s.access.Unlock()
|
|
return err
|
|
}
|
|
child, loaded := s.children[component]
|
|
if !loaded {
|
|
childScope := NewScope(s.ctx, s.logger)
|
|
child = &scopeChild{scope: childScope}
|
|
child.cleanup = s.cleanups.PushBack(func() error {
|
|
done := LogElapsed(s.logger, "close ", name)
|
|
monitor := taskmonitor.New(s.logger, C.StopTimeout)
|
|
monitor.Start("close ", name)
|
|
closeErr := childScope.Close()
|
|
monitor.Finish()
|
|
done()
|
|
if closeErr != nil {
|
|
return E.Cause(closeErr, "close ", name)
|
|
}
|
|
return nil
|
|
})
|
|
s.children[component] = child
|
|
}
|
|
s.access.Unlock()
|
|
done := LogElapsed(s.logger, stage, " ", name)
|
|
monitor := taskmonitor.New(s.logger, C.StartTimeout)
|
|
monitor.Start(stage, " ", name)
|
|
err = component.Start(stage, child.scope)
|
|
monitor.Finish()
|
|
done()
|
|
if err != nil {
|
|
return E.Cause(err, stage, " ", name)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Scope) Remove(component Lifecycle) error {
|
|
s.access.Lock()
|
|
child, loaded := s.children[component]
|
|
if !loaded {
|
|
s.access.Unlock()
|
|
return nil
|
|
}
|
|
delete(s.children, component)
|
|
cleanup := s.cleanups.Remove(child.cleanup)
|
|
s.access.Unlock()
|
|
return cleanup()
|
|
}
|
|
|
|
func (s *Scope) Close() error {
|
|
s.access.Lock()
|
|
s.cancel()
|
|
var cleanups []func() error
|
|
for element := s.cleanups.Back(); element != nil; element = element.Prev() {
|
|
cleanups = append(cleanups, element.Value)
|
|
}
|
|
s.cleanups.Init()
|
|
s.children = nil
|
|
s.access.Unlock()
|
|
var err error
|
|
for _, cleanup := range cleanups {
|
|
err = E.Errors(err, cleanup())
|
|
}
|
|
return err
|
|
}
|
|
|
|
func LogElapsed(logger log.ContextLogger, description ...any) func() {
|
|
prefix := F.ToString(description...)
|
|
startTime := time.Now()
|
|
timer := time.AfterFunc(time.Second, func() {
|
|
logger.Trace(prefix, "...")
|
|
})
|
|
return func() {
|
|
if timer.Stop() {
|
|
return
|
|
}
|
|
logger.Trace(prefix, " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
|
}
|
|
}
|