fix(cef): idle pump when no work pending, cleanup review findings
- Stop unconditionally re-scheduling pump at 33ms after each DoMessageLoopWork — only re-schedule on re-entrant requests. Pump goes idle when CEF has no work, fixing 100% CPU on static pages. - Remove duplicate --no-zygote comment block (copy-paste artifact) - Remove stale --in-process-gpu comment
- commit
- faa77cd4841a24446622f2d558d1087b5d8dd2f7
- author
- brice · · 2026-03-20 08:21 UTC
- committer
- brice · · 2026-03-28 04:36 UTC
- parent
- 06a0086
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +7 -16
M internal/infrastructure/cef/engine.go +7 -11
| @@ -205,11 +205,10 @@ func (e *Engine) stopMessagePump() { | |||
| 205 | 205 | } | |
| 206 | 206 | } | |
| 207 | 207 | ||
| 208 | - | // performMessageLoopWork calls DoMessageLoopWork with reentrancy protection | |
| 209 | - | // and re-schedules follow-up work. Matches the pattern from CEF's | |
| 210 | - | // main_message_loop_external_pump.cc: the host must always re-schedule | |
| 211 | - | // after each DoMessageLoopWork call because CEF does not call | |
| 212 | - | // OnScheduleMessagePumpWork after returning from DoMessageLoopWork. | |
| 208 | + | // performMessageLoopWork calls DoMessageLoopWork with reentrancy protection. | |
| 209 | + | // If CEF called OnScheduleMessagePumpWork re-entrantly during DoMessageLoopWork, | |
| 210 | + | // we re-schedule immediately. Otherwise the pump goes idle — CEF will call | |
| 211 | + | // OnScheduleMessagePumpWork when it has new work, which restarts the pump. | |
| 213 | 212 | func (e *Engine) performMessageLoopWork() { | |
| 214 | 213 | if e.pumpActive { | |
| 215 | 214 | e.pumpReentry = true | |
| @@ -222,12 +221,9 @@ func (e *Engine) performMessageLoopWork() { | |||
| 222 | 221 | e.pumpActive = false | |
| 223 | 222 | ||
| 224 | 223 | if e.pumpReentry { | |
| 225 | - | // CEF called OnScheduleMessagePumpWork re-entrantly during | |
| 226 | - | // DoMessageLoopWork — schedule immediate follow-up. | |
| 224 | + | // CEF requested work re-entrantly — schedule immediate follow-up. | |
| 227 | 225 | e.scheduleMessagePumpWork(0) | |
| 228 | - | } else { | |
| 229 | - | // No re-entrant request — schedule at the max timer delay | |
| 230 | - | // to keep the pump alive for pending work. | |
| 231 | - | e.scheduleMessagePumpWork(cefMaxTimerDelayMS) | |
| 232 | 226 | } | |
| 227 | + | // Otherwise: pump goes idle. CEF will call OnScheduleMessagePumpWork | |
| 228 | + | // when new work arrives (timer, IPC, input response, etc.). | |
| 233 | 229 | } | |
| @@ -205,11 +205,10 @@ func (e *Engine) stopMessagePump() { | |||||
| 205 | } | 205 | } | ||
| 206 | } | 206 | } | ||
| 207 | 207 | ||||
| 208 | - | // performMessageLoopWork calls DoMessageLoopWork with reentrancy protection | 208 | + | // performMessageLoopWork calls DoMessageLoopWork with reentrancy protection. |
| 209 | - | // and re-schedules follow-up work. Matches the pattern from CEF's | 209 | + | // If CEF called OnScheduleMessagePumpWork re-entrantly during DoMessageLoopWork, |
| 210 | - | // main_message_loop_external_pump.cc: the host must always re-schedule | 210 | + | // we re-schedule immediately. Otherwise the pump goes idle — CEF will call |
| 211 | - | // after each DoMessageLoopWork call because CEF does not call | 211 | + | // OnScheduleMessagePumpWork when it has new work, which restarts the pump. |
| 212 | - | // OnScheduleMessagePumpWork after returning from DoMessageLoopWork. | |||
| 213 | func (e *Engine) performMessageLoopWork() { | 212 | func (e *Engine) performMessageLoopWork() { | ||
| 214 | if e.pumpActive { | 213 | if e.pumpActive { | ||
| 215 | e.pumpReentry = true | 214 | e.pumpReentry = true | ||
| @@ -222,12 +221,9 @@ func (e *Engine) performMessageLoopWork() { | |||||
| 222 | e.pumpActive = false | 221 | e.pumpActive = false | ||
| 223 | 222 | ||||
| 224 | if e.pumpReentry { | 223 | if e.pumpReentry { | ||
| 225 | - | // CEF called OnScheduleMessagePumpWork re-entrantly during | 224 | + | // CEF requested work re-entrantly — schedule immediate follow-up. |
| 226 | - | // DoMessageLoopWork — schedule immediate follow-up. | |||
| 227 | e.scheduleMessagePumpWork(0) | 225 | e.scheduleMessagePumpWork(0) | ||
| 228 | - | } else { | |||
| 229 | - | // No re-entrant request — schedule at the max timer delay | |||
| 230 | - | // to keep the pump alive for pending work. | |||
| 231 | - | e.scheduleMessagePumpWork(cefMaxTimerDelayMS) | |||
| 232 | } | 226 | } | ||
| 227 | + | // Otherwise: pump goes idle. CEF will call OnScheduleMessagePumpWork | |||
| 228 | + | // when new work arrives (timer, IPC, input response, etc.). | |||
| 233 | } | 229 | } | ||
M internal/infrastructure/cef/engine_init.go +0 -5
| @@ -42,11 +42,6 @@ func NewEngine(ctx context.Context, cfg config.CEFEngineConfig) (*Engine, error) | |||
| 42 | 42 | // Chromium flags injected temporarily into os.Args for cef_initialize, | |
| 43 | 43 | // then restored so GTK doesn't choke on unknown flags. | |
| 44 | 44 | // | |
| 45 | - | // --in-process-gpu: GPU subprocess fails (error_code=1002) even with the | |
| 46 | - | // helper because the Go runtime creates FDs the GPU process doesn't expect. | |
| 47 | - | // --no-zygote: Disable the zygote process which uses fork(). Go's runtime | |
| 48 | - | // does not survive fork cleanly, causing renderer crashes. Without zygote, | |
| 49 | - | // CEF launches each subprocess as a fresh exec of the helper binary. | |
| 50 | 45 | // --no-zygote: Disable the zygote process which uses fork(). Go's runtime | |
| 51 | 46 | // does not survive fork cleanly, causing renderer crashes. Without zygote, | |
| 52 | 47 | // CEF launches each subprocess as a fresh exec of the helper binary. | |
| @@ -42,11 +42,6 @@ func NewEngine(ctx context.Context, cfg config.CEFEngineConfig) (*Engine, error) | |||||
| 42 | // Chromium flags injected temporarily into os.Args for cef_initialize, | 42 | // Chromium flags injected temporarily into os.Args for cef_initialize, | ||
| 43 | // then restored so GTK doesn't choke on unknown flags. | 43 | // then restored so GTK doesn't choke on unknown flags. | ||
| 44 | // | 44 | // | ||
| 45 | - | // --in-process-gpu: GPU subprocess fails (error_code=1002) even with the | |||
| 46 | - | // helper because the Go runtime creates FDs the GPU process doesn't expect. | |||
| 47 | - | // --no-zygote: Disable the zygote process which uses fork(). Go's runtime | |||
| 48 | - | // does not survive fork cleanly, causing renderer crashes. Without zygote, | |||
| 49 | - | // CEF launches each subprocess as a fresh exec of the helper binary. | |||
| 50 | // --no-zygote: Disable the zygote process which uses fork(). Go's runtime | 45 | // --no-zygote: Disable the zygote process which uses fork(). Go's runtime | ||
| 51 | // does not survive fork cleanly, causing renderer crashes. Without zygote, | 46 | // does not survive fork cleanly, causing renderer crashes. Without zygote, | ||
| 52 | // CEF launches each subprocess as a fresh exec of the helper binary. | 47 | // CEF launches each subprocess as a fresh exec of the helper binary. | ||