platform: Improve iOS OOM killer
This commit is contained in:
138
service/oomkiller/service.go
Normal file
138
service/oomkiller/service.go
Normal file
@@ -0,0 +1,138 @@
|
||||
//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()
|
||||
}
|
||||
}
|
||||
39
service/oomkiller/service_stub.go
Normal file
39
service/oomkiller/service_stub.go
Normal file
@@ -0,0 +1,39 @@
|
||||
//go:build !darwin || !cgo
|
||||
|
||||
package oomkiller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
boxService "github.com/sagernet/sing-box/adapter/service"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func RegisterService(registry *boxService.Registry) {
|
||||
boxService.Register[option.OOMKillerServiceOptions](registry, C.TypeOOMKiller, NewService)
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
boxService.Adapter
|
||||
}
|
||||
|
||||
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.OOMKillerServiceOptions) (adapter.Service, error) {
|
||||
return &Service{
|
||||
Adapter: boxService.NewAdapter(C.TypeOOMKiller, tag),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) Start(stage adapter.StartStage) error {
|
||||
if stage != adapter.StartStateStart {
|
||||
return nil
|
||||
}
|
||||
return E.New("memory pressure monitoring is not available on this platform")
|
||||
}
|
||||
|
||||
func (s *Service) Close() error {
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user