Repository navigation
Prevent window.postMessage traffic from starving due timers #293
Description
Activity
- addedbugSomething isn't workingSomething isn't workingvscode-oss/plannedPlanned for the AppScene/WebScene VS Code OSS integrationPlanned for the AppScene/WebScene VS Code OSS integration
on Sep 17, 2026 Local candidate
b485f6aa(stacked on #287 candidateb75d3795, both unpushed) adds due-timer arbitration against the independently refilling window-message task source.The exact reduced failure now passes twice:
- run 1: 20 cycles, protocol-flush p95 0.134 ms, max 0.268 ms, window tasks before flush max 2
- run 2: p95 0.184 ms, max 0.323 ms, window max 2, Worker max 1, MessagePort max 1, WebSocket max 0
- 1,071,425-byte body; Ready p95 4.112 ms; Initialized p95 12.293 ms; send-return p95 0.905 ms
- 342 timers, 648 microtask checkpoints, 47 signalled wakes, 2 scene builds
- V8 heap after low-memory collection: 657,260 bytes; process RSS max: 121,307,136 bytes
Chromium 151 control passes 45/45 with protocol-flush p95 0.100 ms, max 0.300 ms, and zero competing messages before the flush. Native adjacent gates pass: post-message style batching, Worker configuration ordering, idle V8 platform, ServiceWorker clients (100 cycles), and cooperative iframe hydration.
Evidence:
/private/tmp/webscene-293-window-message-candidate.log, SHA-256143320b7b0e4278cf6bc2c3e8cb83cd71bb11f1f0c83437532e869e4f4aaf7b0/private/tmp/webscene-293-window-message-candidate-repeat.log, SHA-256bc0a04287d46429c2e7891faac557675cc00181ce7cb651ce5bd585fc223a3b9/private/tmp/webscene-293-chromium-control.log, SHA-25670bba0c382ad165f41626b8365b9f3a39494b1a967fd9fc4786185a0c873d1e9/private/tmp/webscene-293-adjacent-gates.log, SHA-2562f1887c661af2a69ea634c86ebe805ccf45d6cc6bd14365eb77d843544e48138
The candidate remains local. Draft #292 still collides with the dispatcher and must land or split before any push/PR. Unchanged Code OSS #252 remains the required product gate.
Unchanged Code OSS product validation rejects
b485f6aaas sufficient for #252, despite the reduced window-message oracle passing:Phase Timestamp / duration Remote transport connected 1789662766314Ready / first protocol message 1789662767887Initialization data created 1789662768385(497.258 ms)Serialized 1789662768392(7.464 ms, 1,070,402 bytes)Protocol send requested 1789662768392Native WebSocket.sendentered1789662771870(+3478 ms)Native send returned 0.815 ms Send microtask 0.857 ms, bufferedAmount=0Remote Initialized 1789662771989(Ready→Initialized ~4102 ms)The exact remote root, provider registration, five resolved/model children, and five Explorer rows remained correct, but the two-second contract failed. Therefore
window.postMessageis a proven generic starvation source, yet it is not the sole owner of the packaged product delay. The candidate remains local/unpushed and this issue cannot claim the product acceptance checkbox.App RSS was 732,288 KiB; observed process-tree RSS was 1,360,624 KiB. App, guardian, server, extension host, agent host, watcher, and Copilot descendants were terminated; port 56322 is free.
Evidence:
/private/tmp/vscode-252-product-b485f6aa.log, SHA-256cb9b7c5c843da4badc2b8c7e35c2850b41598c70b7510afce7c94bee5b5a2a26.The corrected packaged histogram makes #293 secondary to #297 for the current product gate. The decisive 50 ms timer was overdue 2459.903 ms: one fetch task consumed 2457.097 ms, while window-message work was only 0.107 ms. Within that fetch task, the built-in
light_vs.jsonpromise microtask checkpoint consumed 2424.455 ms.#293's reduced generic defect and local fix remain valid, but it does not qualify unchanged #252 and must not be presented as the current product owner.
The generic scheduler fix for this reduced starvation class has now landed through PR #347 at
dbd7351a88415d369ea322c692153474fb7a933c.The implementation alternates due timers with all observable ready task sources, including window-message traffic, and preserves run-to-completion plus the microtask checkpoint. Exact-base product-shaped validation measured the 1.07 MiB protocol flush at 0.157 ms p95 / 0.274 ms max, with no more than 2 competing tasks first and queue high-water 4. This issue remains open until unchanged packaged Code OSS #252 confirms the downstream product timing.
Exact merged-package closure evidence — 21 September 2026
The current installed-SDK Release now passes the shared unchanged-product gate that this issue was waiting for.
Exact inputs:
- AppScene
cd0a02ecd15f0a635bccc242cdd657888db39e25 - WebScene
a99eaf23aef5e5525b0a58c5635fa8a431b604f1 - unchanged Code OSS
645f29cc3176500b4b5762ba887cf2a7f0ffdf2c - SDK manifest SHA-256
475c5652aed411cb98867ad79074e75096534adce6f607cbe1a11f5b2b39ab9b - executable SHA-256
9c3404b01537cca336717492030f548b15180fc5c3d1f684185963808d430b4f - product log SHA-256
c78d19651bf5615b9db7595ea2aa16bc252ad1395d19b83b610088639c864fc2
Unchanged-product result:
- selected URI equals the only workspace root;
- stock remote provider registered;
- exact resolved/model/rendered child set:
.hidden.txt,readme-link,README.md,subdir,unicodé.txt; - resolved/model/visible counts: 5/5/5;
- maximum Explorer DOM nodes: 64;
- provider resolve: 757.40 ms;
- first-child paint: 1,880.02 ms, inside the 2-second product budget;
- observer sampling maximum: 15.37 ms;
- protocol: 79 FileReader reads / 11,801,046 bytes, 47.41 ms maximum FileReader completion, 0.067 ms maximum Blob conversion;
- remote transport → Ready: 939 ms; Ready → Initialized: 295 ms;
- after bounded termination, no AppScene host, bundled server, extension-host, or helper process remained.
The closing implementation chain is merged: shared task-source/protocol fairness, dedicated FileReader scheduling, per-socket WebSocket fairness (#888), metadata-only diagnostics (#889), and lazy binary Blob compatibility-string expansion (#890, merge
a99eaf23). The 12 MiB focused Blob gate measured 0.867 ms construction without requested text expansion versus 55.79 ms with explicit expansion (~64.4×), preserved the exact snapshot/string result, retained cross-socket ordering, and finished with flat settled heap.The wider workspace/watcher/dependent-service matrix remains tracked by #252. This issue's shared implementation and exact-product latency gate are complete.
- AppScene
Reopened after repeated exact-package run — 21 September 2026
The first exact merged-package run passed at 1,880.02 ms, but a second independent cold run on the same AppScene cd0a02e / WebScene a99eaf2 / unchanged Code OSS 645f29cc package painted at 2,179.19 ms. Provider resolve remained bounded at 1,110.87 ms, the exact root/provider/five resolved/model/rendered children remained correct, and maximum Explorer DOM nodes remained 64.
Because this issue explicitly requires the unchanged-product first-child result within two seconds across repeated cold/warm qualification, one passing run is insufficient. The issue is reopened pending a multi-cycle p50/p95 gate and attribution of the remaining approximately 179 ms breach. The already merged direct scheduler/Blob regressions remain valid and are not reverted.
Completed by the shared merged scheduler/protocol chain plus WebScene PR #891.
The exact unchanged-product qualification passed 20/20 launches with the requested root, provider, five resolved/model/rendered children, and first-child paint under 2 seconds. Resolve p95 is 762.64 ms and paint p95 is 1860.12 ms. Broader workspace-file, multi-root, interaction, visual, dependent-service, and lifecycle acceptance remains tracked in #252.
Completed — 21 September 2026
The shared scheduler/protocol implementation and WebScene PR #891 now satisfy the current installed-package proof that kept this issue open. On unchanged Code OSS 645f29cc, tested WebScene change 1412283 (merged without code changes as d3c030f), and AppScene cd0a02e, 20/20 launches retained the exact remote root, registered provider, five resolved children, five Explorer-model children, five visible rows, and 64-node DOM bound.
Resolve is 751.61 ms p50 / 762.64 ms p95 / 797.49 ms maximum. First-child paint is 1828.19 ms p50 / 1860.12 ms p95 / 1870.24 ms maximum; every launch satisfies the 2-second gate. Routed selector invalidation behavior remains covered by the focused native regressions, and the direct-subject optimization has an explicit A/B escape hatch.
The remaining workspace-file/multi-root, interaction, dependent-service, visual, and repeated lifecycle matrix stays in #252. No duplicate scheduler implementation remains scheduled.
Parent scheduler issue: #287
Remote handshake attribution: #289
Workspace acceptance: #252
Runtime collision/dependency: draft PR #292
Problem
WebScene drains
window.postMessagetasks after the Worker/ServiceWorker/MessagePort/WebSocket rotation but before any due timer. A message handler that refills its own window-message queue can therefore prevent a zero-delay timer from running indefinitely, even after the asynchronous-message fairness fix proven locally for #287.This matches the unchanged Code OSS remote extension-host bootstrap. Its protocol writer requests a timer-coalesced 1,071,424-byte initialization send, while iframe/window messaging remains active. The timer request is retained immediately, but native
WebSocket.sendis not entered for several seconds.Reduced evidence
On exact WebScene main
1bd8596e3dc07a3281204504acad379eb1f6bc78plus local unpushed #287 candidateb75d37958ef7ce3f73a5b08de6b0516b0f8f57eb, the product-shaped 20-cycle oracle adds one self-refillingwindow.postMessagesource alongside Worker, MessagePort, WebSocket, FileReader, and the 1,071,425-byte body:Without the window source, the same candidate reports maximum 0.204 ms and p95 0.157 ms. The packaged unchanged-Code trace independently measured protocol-send request → native
WebSocket.sendentry at 3698 ms, followed by a 0.783 ms send and 0.834 ms microtask. The close agreement identifies task arbitration rather than wire throughput.Evidence:
/private/tmp/webscene-289-b75d3795-window-message-oracle.log(SHA-256d2f77ef271fe8863b36370e77feba57c730939bcf7844e8a6f955777fcc5416d) and/private/tmp/vscode-252-product-b75d3795.log(SHA-256f58b40413fe5dd30567d0d6a3d207b668fd07bea96b594aae9c5568509a38252).Scope
Make due timers fair with the
window.postMessagetask source while preserving FIFO ordering, origin/source/transfer semantics, bounded queue limits, and the four-source Worker/ServiceWorker/MessagePort/WebSocket rotation. Do not alter transferredMessagePortlifetime ownership or add Code OSS special cases.Draft #292 touches the same runtime dispatcher. Its DOM-listener retirement work is semantically independent, but this implementation must wait for #292 to merge or split, then rebase without regressing #281's four-source rotation.
Acceptance
window.postMessageorder,source,origin, transferred ports, microtask checkpoints, and existing queue byte/count caps remain correct.