@@ -296,6 +296,91 @@ reader.read() ──► StreamRead(callback_id, stream_id)
296296
297297---
298298
299+ ## Two Loops Architecture
300+
301+ There are actually ** two separate loops** that work together:
302+
303+ ### 1. Scheduler Loop (background task)
304+
305+ Runs continuously in the background, spawning async tasks:
306+
307+ ``` rust
308+ // runtime/mod.rs - run_event_loop()
309+ async fn run_event_loop (scheduler_rx , callback_tx , ops ) {
310+ loop {
311+ select! {
312+ msg = scheduler_rx . recv () => {
313+ match msg {
314+ FetchStreaming (id , req ) => {
315+ tokio :: spawn (async {
316+ let result = ops . handle (Fetch (req )). await ;
317+ callback_tx . send (FetchSuccess (id , result ));
318+ });
319+ }
320+ ScheduleTimeout (id , ms ) => { /* spawn timer */ }
321+ }
322+ }
323+ }
324+ }
325+ }
326+ ```
327+
328+ ### 2. Execution Loop (in exec())
329+
330+ Polls for results and checks termination conditions:
331+
332+ ``` rust
333+ // worker.rs - exec()
334+ async fn exec (& mut self , task : Task ) {
335+ self . trigger_fetch_event (request ); // Start JS execution
336+
337+ for iteration in 0 .. 5000 {
338+ // Check termination (timeout, CPU limit)
339+ if wall_guard . was_triggered () {
340+ return Err (WallClockTimeout );
341+ }
342+
343+ // Process available callbacks (NON-BLOCKING)
344+ self . runtime. process_callbacks ();
345+
346+ // Check if response is ready
347+ if response_ready { break ; }
348+
349+ // Yield to let scheduler run
350+ tokio :: time :: sleep (duration ). await ;
351+ }
352+ }
353+ ```
354+
355+ ### Why Two Loops?
356+
357+ ```
358+ ┌──────────────────────────────────────────────────────────────────┐
359+ │ exec() loop │
360+ │ │
361+ │ ┌─────────┐ ┌───────────────────┐ ┌─────────┐ │
362+ │ │ Check │───►│ process_callbacks │───►│ Check │───► sleep │
363+ │ │ timeout │ │ (try_recv) │ │ ready │ │
364+ │ └─────────┘ └───────────────────┘ └─────────┘ │
365+ │ │ ▲ │
366+ │ │ │ results │
367+ │ │ │ │
368+ │ ▼ │ │
369+ │ ┌──────────────────────────────────────────────────────────┐ │
370+ │ │ Scheduler (background task) │ │
371+ │ │ │ │
372+ │ │ recv() ──► spawn fetch ──► await reqwest ──► send() │ │
373+ │ └──────────────────────────────────────────────────────────┘ │
374+ └──────────────────────────────────────────────────────────────────┘
375+ ```
376+
377+ - ** Scheduler** : Handles async I/O (blocking ` recv().await ` )
378+ - ** exec() loop** : Polls results, enforces timeouts, controls V8
379+
380+ The ` sleep().await ` in exec() is crucial - it yields control to tokio, allowing the scheduler to make progress on pending operations.
381+
382+ ---
383+
299384## Integration Points
300385
301386### process_callbacks()
@@ -311,7 +396,7 @@ pub fn process_callbacks(&mut self) {
311396 // 1. V8 internal tasks (Atomics, WebAssembly, etc.)
312397 while v8 :: Platform :: pump_message_loop (platform , scope , false ) {}
313398
314- // 2. Our custom callbacks
399+ // 2. Our custom callbacks (NON-BLOCKING: try_recv returns immediately)
315400 while let Ok (msg ) = self . callback_rx. try_recv () {
316401 match msg { /* ... */ }
317402 }
@@ -321,6 +406,8 @@ pub fn process_callbacks(&mut self) {
321406}
322407```
323408
409+ ** Important** : ` try_recv() ` is non-blocking - it returns ` Err(Empty) ` immediately if no messages are available. This allows the exec() loop to check for timeouts and response readiness without waiting.
410+
324411### OperationsHandler
325412
326413All I/O goes through the Runner's ` OperationsHandler ` :
@@ -348,7 +435,8 @@ This allows the Runner to:
348435
349436| File | Purpose |
350437| ------------------------------------------------------ | --------------------------------------------- |
351- | ` openworkers-runtime-v8/src/runtime/mod.rs ` | Runtime struct, event loop, process_callbacks |
438+ | ` openworkers-runtime-v8/src/worker.rs ` | Worker struct, exec() loop |
439+ | ` openworkers-runtime-v8/src/runtime/mod.rs ` | Runtime struct, scheduler, process_callbacks |
352440| ` openworkers-runtime-v8/src/runtime/bindings/ ` | Native V8 functions |
353441| ` openworkers-runtime-v8/src/runtime/stream_manager.rs ` | Stream coordination |
354442| ` openworkers-core/src/ops.rs ` | Operation, OperationResult enums |
0 commit comments