detached faa77cd

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 VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · 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() {
205205 }
206206 }
207207
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.
213212 func (e *Engine) performMessageLoopWork() {
214213 if e.pumpActive {
215214 e.pumpReentry = true
@@ -222,12 +221,9 @@ func (e *Engine) performMessageLoopWork() {
222221 e.pumpActive = false
223222
224223 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.
227225 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)
232226 }
227+ // Otherwise: pump goes idle. CEF will call OnScheduleMessagePumpWork
228+ // when new work arrives (timer, IPC, input response, etc.).
233229 }
@@ -205,11 +205,10 @@ func (e *Engine) stopMessagePump() {
205 }205 }
206 }206 }
207 207
208-// performMessageLoopWork calls DoMessageLoopWork with reentrancy protection208+// performMessageLoopWork calls DoMessageLoopWork with reentrancy protection.
209-// and re-schedules follow-up work. Matches the pattern from CEF's209+// If CEF called OnScheduleMessagePumpWork re-entrantly during DoMessageLoopWork,
210-// main_message_loop_external_pump.cc: the host must always re-schedule210+// we re-schedule immediately. Otherwise the pump goes idle — CEF will call
211-// after each DoMessageLoopWork call because CEF does not call211+// 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 = true214 e.pumpReentry = true
@@ -222,12 +221,9 @@ func (e *Engine) performMessageLoopWork() {
222 e.pumpActive = false221 e.pumpActive = false
223 222
224 if e.pumpReentry {223 if e.pumpReentry {
225- // CEF called OnScheduleMessagePumpWork re-entrantly during224+ // 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)
4242 // Chromium flags injected temporarily into os.Args for cef_initialize,
4343 // then restored so GTK doesn't choke on unknown flags.
4444 //
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.
5045 // --no-zygote: Disable the zygote process which uses fork(). Go's runtime
5146 // does not survive fork cleanly, causing renderer crashes. Without zygote,
5247 // 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 runtime45 // --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.

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close