diff --git a/common/sleep/manager.go b/common/sleep/manager.go new file mode 100644 index 00000000..a668e76f --- /dev/null +++ b/common/sleep/manager.go @@ -0,0 +1,43 @@ +package sleep + +import ( + "sync" +) + +type Manager struct { + access sync.Mutex + done chan struct{} +} + +func NewManager() *Manager { + closedChan := make(chan struct{}) + close(closedChan) + return &Manager{ + done: closedChan, + } +} + +func (m *Manager) Sleep() { + m.access.Lock() + defer m.access.Unlock() + select { + case <-m.done: + default: + return + } + m.done = make(chan struct{}) +} + +func (m *Manager) Wake() { + m.access.Lock() + defer m.access.Unlock() + select { + case <-m.done: + default: + close(m.done) + } +} + +func (m *Manager) Active() <-chan struct{} { + return m.done +} diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index f37f8408..0b63659e 100644 --- a/experimental/libbox/service.go +++ b/experimental/libbox/service.go @@ -8,6 +8,7 @@ import ( "github.com/sagernet/sing-box" "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/process" + "github.com/sagernet/sing-box/common/sleep" "github.com/sagernet/sing-box/common/urltest" "github.com/sagernet/sing-box/experimental/libbox/internal/procfs" "github.com/sagernet/sing-box/experimental/libbox/platform" @@ -22,9 +23,10 @@ import ( ) type BoxService struct { - ctx context.Context - cancel context.CancelFunc - instance *box.Box + ctx context.Context + cancel context.CancelFunc + instance *box.Box + sleepManager *sleep.Manager } func NewService(configContent string, platformInterface PlatformInterface) (*BoxService, error) { @@ -35,6 +37,8 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box ctx, cancel := context.WithCancel(context.Background()) ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID) ctx = service.ContextWithPtr(ctx, urltest.NewHistoryStorage()) + sleepManager := sleep.NewManager() + ctx = service.ContextWithPtr(ctx, sleepManager) instance, err := box.New(box.Options{ Context: ctx, Options: options, @@ -45,9 +49,10 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box return nil, E.Cause(err, "create service") } return &BoxService{ - ctx: ctx, - cancel: cancel, - instance: instance, + ctx: ctx, + cancel: cancel, + instance: instance, + sleepManager: sleepManager, }, nil } @@ -60,6 +65,15 @@ func (s *BoxService) Close() error { return s.instance.Close() } +func (s *BoxService) Sleep() { + s.sleepManager.Sleep() + _ = s.instance.Router().ResetNetwork() +} + +func (s *BoxService) Wake() { + s.sleepManager.Wake() +} + var _ platform.Interface = (*platformInterfaceWrapper)(nil) type platformInterfaceWrapper struct { diff --git a/outbound/urltest.go b/outbound/urltest.go index fce7bd05..1c95084d 100644 --- a/outbound/urltest.go +++ b/outbound/urltest.go @@ -8,6 +8,7 @@ import ( "time" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/sleep" "github.com/sagernet/sing-box/common/urltest" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" @@ -143,15 +144,16 @@ func (s *URLTest) InterfaceUpdated() error { } type URLTestGroup struct { - ctx context.Context - router adapter.Router - logger log.Logger - outbounds []adapter.Outbound - link string - interval time.Duration - tolerance uint16 - history *urltest.HistoryStorage - checking atomic.Bool + ctx context.Context + router adapter.Router + logger log.Logger + outbounds []adapter.Outbound + link string + interval time.Duration + tolerance uint16 + history *urltest.HistoryStorage + checking atomic.Bool + sleepManager *sleep.Manager access sync.Mutex ticker *time.Ticker @@ -173,15 +175,16 @@ func NewURLTestGroup(ctx context.Context, router adapter.Router, logger log.Logg history = urltest.NewHistoryStorage() } return &URLTestGroup{ - ctx: ctx, - router: router, - logger: logger, - outbounds: outbounds, - link: link, - interval: interval, - tolerance: tolerance, - history: history, - close: make(chan struct{}), + ctx: ctx, + router: router, + logger: logger, + outbounds: outbounds, + link: link, + interval: interval, + tolerance: tolerance, + history: history, + close: make(chan struct{}), + sleepManager: service.PtrFromContext[sleep.Manager](ctx), } } @@ -263,6 +266,9 @@ func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound { func (g *URLTestGroup) loopCheck() { go g.CheckOutbounds(true) for { + if g.sleepManager != nil { + <-g.sleepManager.Active() + } select { case <-g.close: return