mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-11 17:47:20 +10:00
139 lines
3.5 KiB
Go
139 lines
3.5 KiB
Go
//go:build darwin && cgo
|
|
|
|
package oomkiller
|
|
|
|
/*
|
|
#include <dispatch/dispatch.h>
|
|
|
|
static dispatch_source_t memoryPressureSource;
|
|
|
|
extern void goMemoryPressureCallback(unsigned long status);
|
|
|
|
static void startMemoryPressureMonitor() {
|
|
memoryPressureSource = dispatch_source_create(
|
|
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE,
|
|
0,
|
|
DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL,
|
|
dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)
|
|
);
|
|
dispatch_source_set_event_handler(memoryPressureSource, ^{
|
|
unsigned long status = dispatch_source_get_data(memoryPressureSource);
|
|
goMemoryPressureCallback(status);
|
|
});
|
|
dispatch_activate(memoryPressureSource);
|
|
}
|
|
|
|
static void stopMemoryPressureMonitor() {
|
|
if (memoryPressureSource) {
|
|
dispatch_source_cancel(memoryPressureSource);
|
|
memoryPressureSource = NULL;
|
|
}
|
|
}
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"context"
|
|
runtimeDebug "runtime/debug"
|
|
"sync"
|
|
|
|
"github.com/sagernet/sing-box/adapter"
|
|
boxService "github.com/sagernet/sing-box/adapter/service"
|
|
boxConstant "github.com/sagernet/sing-box/constant"
|
|
"github.com/sagernet/sing-box/log"
|
|
"github.com/sagernet/sing-box/option"
|
|
"github.com/sagernet/sing/common/memory"
|
|
"github.com/sagernet/sing/service"
|
|
)
|
|
|
|
func RegisterService(registry *boxService.Registry) {
|
|
boxService.Register[option.OOMKillerServiceOptions](registry, boxConstant.TypeOOMKiller, NewService)
|
|
}
|
|
|
|
var (
|
|
globalAccess sync.Mutex
|
|
globalServices []*Service
|
|
)
|
|
|
|
type Service struct {
|
|
boxService.Adapter
|
|
logger log.ContextLogger
|
|
router adapter.Router
|
|
}
|
|
|
|
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.OOMKillerServiceOptions) (adapter.Service, error) {
|
|
return &Service{
|
|
Adapter: boxService.NewAdapter(boxConstant.TypeOOMKiller, tag),
|
|
logger: logger,
|
|
router: service.FromContext[adapter.Router](ctx),
|
|
}, nil
|
|
}
|
|
|
|
func (s *Service) Start(stage adapter.StartStage) error {
|
|
if stage != adapter.StartStateStart {
|
|
return nil
|
|
}
|
|
globalAccess.Lock()
|
|
isFirst := len(globalServices) == 0
|
|
globalServices = append(globalServices, s)
|
|
globalAccess.Unlock()
|
|
if isFirst {
|
|
C.startMemoryPressureMonitor()
|
|
}
|
|
s.logger.Info("started memory pressure monitor")
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) Close() error {
|
|
globalAccess.Lock()
|
|
for i, service := range globalServices {
|
|
if service == s {
|
|
globalServices = append(globalServices[:i], globalServices[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
isLast := len(globalServices) == 0
|
|
globalAccess.Unlock()
|
|
if isLast {
|
|
C.stopMemoryPressureMonitor()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//export goMemoryPressureCallback
|
|
func goMemoryPressureCallback(status C.ulong) {
|
|
globalAccess.Lock()
|
|
services := make([]*Service, len(globalServices))
|
|
copy(services, globalServices)
|
|
globalAccess.Unlock()
|
|
if len(services) == 0 {
|
|
return
|
|
}
|
|
criticalFlag := C.ulong(C.DISPATCH_MEMORYPRESSURE_CRITICAL)
|
|
warnFlag := C.ulong(C.DISPATCH_MEMORYPRESSURE_WARN)
|
|
isCritical := status&criticalFlag != 0
|
|
isWarning := status&warnFlag != 0
|
|
var level string
|
|
switch {
|
|
case isCritical:
|
|
level = "critical"
|
|
case isWarning:
|
|
level = "warning"
|
|
default:
|
|
level = "normal"
|
|
}
|
|
for _, s := range services {
|
|
if isCritical {
|
|
s.logger.Error("memory pressure: ", level, ", usage: ", memory.Total()/(1024*1024), " MiB, resetting network")
|
|
s.router.ResetNetwork()
|
|
} else if isWarning {
|
|
s.logger.Warn("memory pressure: ", level, ", usage: ", memory.Total()/(1024*1024), " MiB")
|
|
} else {
|
|
s.logger.Debug("memory pressure: ", level, ", usage: ", memory.Total()/(1024*1024), " MiB")
|
|
}
|
|
}
|
|
if isCritical {
|
|
runtimeDebug.FreeOSMemory()
|
|
}
|
|
}
|