| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- package oomkiller
- import (
- runtimeDebug "runtime/debug"
- "sync"
- "time"
- "github.com/sagernet/sing-box/adapter"
- "github.com/sagernet/sing-box/log"
- "github.com/sagernet/sing/common/memory"
- )
- const (
- defaultChecksBeforeLimit = 4
- defaultMinInterval = 500 * time.Millisecond
- defaultMaxInterval = 10 * time.Second
- defaultSafetyMargin = 5 * 1024 * 1024
- )
- type adaptiveTimer struct {
- logger log.ContextLogger
- router adapter.Router
- memoryLimit uint64
- safetyMargin uint64
- minInterval time.Duration
- maxInterval time.Duration
- checksBeforeLimit int
- useAvailable bool
- access sync.Mutex
- timer *time.Timer
- previousUsage uint64
- lastInterval time.Duration
- }
- type timerConfig struct {
- memoryLimit uint64
- safetyMargin uint64
- minInterval time.Duration
- maxInterval time.Duration
- checksBeforeLimit int
- useAvailable bool
- }
- func newAdaptiveTimer(logger log.ContextLogger, router adapter.Router, config timerConfig) *adaptiveTimer {
- return &adaptiveTimer{
- logger: logger,
- router: router,
- memoryLimit: config.memoryLimit,
- safetyMargin: config.safetyMargin,
- minInterval: config.minInterval,
- maxInterval: config.maxInterval,
- checksBeforeLimit: config.checksBeforeLimit,
- useAvailable: config.useAvailable,
- }
- }
- func (t *adaptiveTimer) start(_ uint64) {
- t.access.Lock()
- defer t.access.Unlock()
- t.startLocked()
- }
- func (t *adaptiveTimer) startNow() {
- t.access.Lock()
- t.startLocked()
- t.access.Unlock()
- t.poll()
- }
- func (t *adaptiveTimer) startLocked() {
- if t.timer != nil {
- return
- }
- t.previousUsage = memory.Total()
- t.lastInterval = t.minInterval
- t.timer = time.AfterFunc(t.minInterval, t.poll)
- }
- func (t *adaptiveTimer) stop() {
- t.access.Lock()
- defer t.access.Unlock()
- t.stopLocked()
- }
- func (t *adaptiveTimer) stopLocked() {
- if t.timer != nil {
- t.timer.Stop()
- t.timer = nil
- }
- }
- func (t *adaptiveTimer) running() bool {
- t.access.Lock()
- defer t.access.Unlock()
- return t.timer != nil
- }
- func (t *adaptiveTimer) poll() {
- t.access.Lock()
- defer t.access.Unlock()
- if t.timer == nil {
- return
- }
- usage := memory.Total()
- delta := int64(usage) - int64(t.previousUsage)
- t.previousUsage = usage
- var remaining uint64
- var triggered bool
- if t.memoryLimit > 0 {
- if usage >= t.memoryLimit {
- remaining = 0
- triggered = true
- } else {
- remaining = t.memoryLimit - usage
- }
- } else if t.useAvailable {
- available := memory.Available()
- if available <= t.safetyMargin {
- remaining = 0
- triggered = true
- } else {
- remaining = available - t.safetyMargin
- }
- } else {
- remaining = 0
- }
- if triggered {
- t.logger.Error("memory threshold reached, usage: ", usage/(1024*1024), " MiB, resetting network")
- t.router.ResetNetwork()
- runtimeDebug.FreeOSMemory()
- }
- var interval time.Duration
- if triggered {
- interval = t.maxInterval
- } else if delta <= 0 {
- interval = t.maxInterval
- } else if t.checksBeforeLimit <= 0 {
- interval = t.maxInterval
- } else {
- timeToLimit := time.Duration(float64(remaining) / float64(delta) * float64(t.lastInterval))
- interval = timeToLimit / time.Duration(t.checksBeforeLimit)
- if interval < t.minInterval {
- interval = t.minInterval
- }
- if interval > t.maxInterval {
- interval = t.maxInterval
- }
- }
- t.lastInterval = interval
- t.timer.Reset(interval)
- }
|