#50418: fix: use fresh LazyNow for OnEndWorkItemImpl to fix TimeKeeper DCHECK
Description of Change
Fixes a flaky DCHECK on Linux CI:
FATAL:base/task/sequence_manager/thread_controller.cc:643] DCHECK failed: !delta.is_negative(). -0.000642 s
Root cause: In ThreadControllerWithMessagePumpImpl::DoWorkImpl, the same LazyNow instance is passed to both DidRunTask() and OnEndWorkItemImpl(). Inside DidRunTask, the LazyNow gets cached early (via BrowserUIThreadScheduler::OnTaskCompleted → RecordTaskEnd) before task observers run. Electron's MicrotasksRunner::DidProcessTask observer then drains V8 microtasks via node::CallbackScope, which can execute arbitrary JS. If that JS triggers nested pump activity (nested RunLoop, sync IPC, etc.), TimeKeeper::last_phase_end_ advances past the cached value. When OnEndWorkItemImpl then uses the stale cached time, it computes a negative delta and hits the DCHECK.
Fix: Use a fresh LazyNow for OnEndWorkItemImpl that samples the clock after all observers have run. This matches the existing comment's intent that "microtasks are extensions of the RunTask" — if so, the work item ends after them, so the time should be sampled then.
Upstreamable: This bug exists whenever any TaskObserver::DidProcessTask triggers nested pump activity, which is not forbidden by the TaskObserver contract. Electron happens to trigger it via MicrotasksRunner, but the fix is generally correct and should be upstreamed to Chromium.
Cost: One extra clock_gettime(CLOCK_MONOTONIC) per task when DidRunTask caches its LazyNow (~20-30ns via VDSO on Linux). When it doesn't cache, no added cost.
Checklist
- PR description included and stakeholders cc'd
-
npm testpasses - Relevant documentation is changed or added (N/A — internal fix)
- PR title follows semantic commit guidelines
Release Notes
Notes: none
Backports
No Backports Requested
This pull request doesn't have any backports requested or created for older release branches.
What are backports?
Backports are copies of changes made to the main branch that are applied to older release branches. They ensure that bug fixes and important changes are available in maintained older versions of Electron.
Semver Impact
Semantic Versioning helps users understand the impact of updates:
- Major (X.y.z): Breaking changes that may require code modifications
- Minor (x.Y.z): New features that maintain backward compatibility
- Patch (x.y.Z): Bug fixes that don't change the API
- None: Changes that don't affect using facing parts of Electron