memory_pressure.go RawHistoryFolder
1package webkit
2
3import (
4 "context"
5
6 "github.com/bnema/dumber/internal/application/port"
7 "github.com/bnema/dumber/internal/logging"
8 "github.com/bnema/puregotk-webkit/webkit"
9)
10
11// MemoryPressureApplier implements port.MemoryPressureApplier for WebKitGTK.
12type MemoryPressureApplier struct{}
13
14// NewMemoryPressureApplier creates a new MemoryPressureApplier.
15func NewMemoryPressureApplier() *MemoryPressureApplier {
16 return &MemoryPressureApplier{}
17}
18
19// ApplyNetworkProcessSettings applies memory pressure settings to the network process.
20// Must be called BEFORE creating any NetworkSession.
21func (*MemoryPressureApplier) ApplyNetworkProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) error {
22 log := logging.FromContext(ctx)
23
24 if cfg == nil || !cfg.IsConfigured() {
25 log.Debug().Msg("no network process memory pressure settings configured")
26 return nil
27 }
28
29 if cfg.ConservativeThreshold > 0 && cfg.StrictThreshold <= 0 {
30 log.Warn().Msg("conservative threshold set without strict threshold; skipping conservative to avoid WebKit assertion")
31 }
32
33 settings := buildMemoryPressureSettings(cfg)
34 if settings == nil {
35 return nil
36 }
37
38 webkit.NetworkSessionSetMemoryPressureSettings(settings)
39 log.Info().
40 Int("limit_mb", cfg.MemoryLimitMB).
41 Float64("poll_sec", cfg.PollIntervalSec).
42 Float64("conservative", cfg.ConservativeThreshold).
43 Float64("strict", cfg.StrictThreshold).
44 Msg("applied network process memory pressure settings")
45
46 return nil
47}
48
49// ApplyWebProcessSettings applies memory pressure settings to web processes.
50// Returns an opaque settings object that should be passed to WebContext creation.
51// Returns nil if no settings are configured.
52func (*MemoryPressureApplier) ApplyWebProcessSettings(ctx context.Context, cfg *port.MemoryPressureConfig) (any, error) {
53 log := logging.FromContext(ctx)
54
55 if cfg == nil || !cfg.IsConfigured() {
56 log.Debug().Msg("no web process memory pressure settings configured")
57 return nil, nil
58 }
59
60 if cfg.ConservativeThreshold > 0 && cfg.StrictThreshold <= 0 {
61 log.Warn().Msg("conservative threshold set without strict threshold; skipping conservative to avoid WebKit assertion")
62 }
63
64 settings := buildMemoryPressureSettings(cfg)
65 if settings == nil {
66 return nil, nil
67 }
68
69 log.Info().
70 Int("limit_mb", cfg.MemoryLimitMB).
71 Float64("poll_sec", cfg.PollIntervalSec).
72 Float64("conservative", cfg.ConservativeThreshold).
73 Float64("strict", cfg.StrictThreshold).
74 Msg("prepared web process memory pressure settings")
75
76 return settings, nil
77}
78
79// buildMemoryPressureSettings creates WebKitMemoryPressureSettings from config.
80// Returns nil if no settings are configured.
81func buildMemoryPressureSettings(cfg *port.MemoryPressureConfig) *webkit.MemoryPressureSettings {
82 if cfg == nil || !cfg.IsConfigured() {
83 return nil
84 }
85
86 settings := webkit.NewMemoryPressureSettings()
87
88 if cfg.MemoryLimitMB > 0 {
89 settings.SetMemoryLimit(uint(cfg.MemoryLimitMB))
90 }
91
92 if cfg.PollIntervalSec > 0 {
93 settings.SetPollInterval(cfg.PollIntervalSec)
94 }
95
96 // Thresholds must be in (0, 1)
97 // IMPORTANT: WebKit asserts that conservative < strict, so set strict first.
98 strict := cfg.StrictThreshold
99 conservative := cfg.ConservativeThreshold
100
101 if strict > 0 && strict < 1 {
102 settings.SetStrictThreshold(strict)
103 }
104
105 // Only set conservative when we also have a strict threshold; otherwise
106 // WebKit's internal default strict value may not be initialized yet.
107 if conservative > 0 && conservative < 1 && strict > 0 {
108 if conservative < strict {
109 settings.SetConservativeThreshold(conservative)
110 }
111 }
112
113 return settings
114}
115
116// Ensure MemoryPressureApplier implements the interface.
117var _ port.MemoryPressureApplier = (*MemoryPressureApplier)(nil)