@@ -19,6 +19,7 @@ import { deriveOfflinePage, deriveOfflinePageCached,
1919import {
2020 createViews , scanAndClaim , onTaskDone ,
2121 READY , F_ON_DEMAND , F_RUN_ON_MAIN ,
22+ F_RUN_WHEN_IDLE , F_UNIQUE_PER_WORKER ,
2223 MAX_LANES ,
2324} from "./sab-scheduler.mjs" ;
2425
@@ -191,6 +192,24 @@ parentPort.on("message", (msg) => {
191192 }
192193} ) ;
193194
195+ // ── Idle-task scan (speculative warmup) ─────────────────────────────────────
196+
197+ function findIdleTask ( views , lane ) {
198+ const count = Atomics . load ( views . taskCount , 0 ) ;
199+ for ( let i = 0 ; i < count ; i ++ ) {
200+ if ( ! ( Atomics . load ( views . flags , i ) & F_RUN_WHEN_IDLE ) ) continue ;
201+ if ( Atomics . load ( views . flags , i ) & F_UNIQUE_PER_WORKER ) {
202+ if ( Atomics . load ( views . perWorkerDone , i * MAX_LANES + lane ) === 0 )
203+ return i ;
204+ } else {
205+ if ( Atomics . load ( views . status , i ) !== READY ) continue ;
206+ if ( Atomics . compareExchange ( views . status , i , READY , CLAIMED ) === READY )
207+ return i ;
208+ }
209+ }
210+ return - 1 ;
211+ }
212+
194213// ── Pull loop ───────────────────────────────────────────────────────────────
195214
196215async function pullLoop ( ) {
@@ -200,6 +219,26 @@ async function pullLoop() {
200219 let taskIdx = scanAndClaim ( views , myLane ) ;
201220
202221 if ( taskIdx === - 1 ) {
222+ // Speculative: run idle-eligible tasks before sleeping
223+ const idleTask = findIdleTask ( views , myLane ) ;
224+ if ( idleTask !== - 1 ) {
225+ const idleMeta = taskMeta [ idleTask ] ;
226+ const idleStart = Date . now ( ) ;
227+ try {
228+ await handlers [ idleMeta . handler ] ( ) ;
229+ } catch ( err ) {
230+ parentPort . postMessage ( { taskFailed : idleTask , message : err . message , stack : err . stack } ) ;
231+ return ;
232+ }
233+ Atomics . store ( views . perWorkerDone , idleTask * MAX_LANES + myLane , 1 ) ;
234+ parentPort . postMessage ( {
235+ warmInit : true ,
236+ timing : { start : idleStart , end : Date . now ( ) } ,
237+ lane : myLane ,
238+ } ) ;
239+ continue ;
240+ }
241+
203242 const gen = Atomics . load ( views . notify , 0 ) ;
204243 // Double-check after reading gen (race: a task may have become
205244 // READY between the failed scan and this load).
0 commit comments