sing-box/route/rule/rule_set_updater.go
2026-06-13 15:50:09 +08:00

87 lines
1.7 KiB
Go

package rule
import (
"context"
"runtime"
"time"
"github.com/sagernet/sing-box/adapter"
)
type RuleSetUpdater struct {
ctx context.Context
cancel context.CancelFunc
ruleSets []*RemoteRuleSet
}
func NewRuleSetUpdater(ctx context.Context, ruleSets []adapter.RuleSet) *RuleSetUpdater {
var remoteRuleSets []*RemoteRuleSet
for _, ruleSet := range ruleSets {
remoteRuleSet, isRemote := ruleSet.(*RemoteRuleSet)
if isRemote {
remoteRuleSets = append(remoteRuleSets, remoteRuleSet)
}
}
if len(remoteRuleSets) == 0 {
return nil
}
ctx, cancel := context.WithCancel(ctx)
return &RuleSetUpdater{
ctx: ctx,
cancel: cancel,
ruleSets: remoteRuleSets,
}
}
func (u *RuleSetUpdater) Start() {
go u.loopUpdate()
}
func (u *RuleSetUpdater) Close() error {
u.cancel()
return nil
}
func (u *RuleSetUpdater) loopUpdate() {
nextUpdates := make([]time.Time, len(u.ruleSets))
for i, ruleSet := range u.ruleSets {
nextUpdates[i] = ruleSet.lastUpdated.Add(ruleSet.updateInterval)
}
timer := time.NewTimer(0)
defer timer.Stop()
for {
select {
case <-u.ctx.Done():
return
case <-timer.C:
}
now := time.Now()
var updated bool
for i, ruleSet := range u.ruleSets {
if now.Before(nextUpdates[i]) {
continue
}
ruleSet.updateOnce()
nextUpdates[i] = now.Add(ruleSet.updateInterval)
updated = true
}
if updated {
runtime.GC()
}
timer.Reset(waitUntilNext(nextUpdates))
}
}
func waitUntilNext(nextUpdates []time.Time) time.Duration {
next := nextUpdates[0]
for _, nextUpdate := range nextUpdates[1:] {
if nextUpdate.Before(next) {
next = nextUpdate
}
}
wait := time.Until(next)
if wait < 0 {
return 0
}
return wait
}