@@ -372,60 +372,63 @@ impl ChipScheduler {
372372 let mut pending: Vec < ChipTask < ' a , PB > > = tasks;
373373
374374 while !pending. is_empty ( ) || tasks_inflight > 0 {
375- // A: Non-blocking drain completions
376- for msg in done_rx. try_iter ( ) {
375+ // First drain any completions already available to free memory immediately.
376+ // This non-blocking path keeps utilization high (and covers the initial loop
377+ // iteration when nothing is running yet), so we handle completions here.
378+ while let Ok ( msg) = done_rx. try_recv ( ) {
377379 if let Err ( e) = handle_completion ( msg, pool, & mut tasks_inflight, "" ) {
378380 drop ( task_tx) ;
379381 return Err ( e) ;
380382 }
381383 }
382384
383- // B: Find first task that fits
384- let mut launched_idx = None ;
385+ // Launch the first pending task whose memory fits; otherwise fall through to wait.
385386 if tasks_inflight < CONCURRENT_PROVING_WORKERS {
386- for ( vec_idx, task) in pending. iter ( ) . enumerate ( ) {
387- if pool
388- . try_book_capacity ( task. estimated_memory_bytes )
387+ if let Some ( vec_idx) = pending. iter ( ) . position ( |task| {
388+ pool. try_book_capacity ( task. estimated_memory_bytes )
389389 . is_some ( )
390- {
391- tracing:: info!(
392- "[scheduler] Launching circuit={}, estimated_mem={:.2}MB, pool_booked={:.2}MB" ,
393- task. circuit_name,
394- task. estimated_memory_bytes as f64 / ( 1024.0 * 1024.0 ) ,
395- pool. get_booked_total( ) as f64 / ( 1024.0 * 1024.0 )
396- ) ;
397- launched_idx = Some ( vec_idx) ;
398- break ;
399- }
390+ } ) {
391+ let task = pending. remove ( vec_idx) ;
392+ tracing:: info!(
393+ "[scheduler] Launching circuit={}, estimated_mem={:.2}MB, pool_booked={:.2}MB" ,
394+ task. circuit_name,
395+ task. estimated_memory_bytes as f64 / ( 1024.0 * 1024.0 ) ,
396+ pool. get_booked_total( ) as f64 / ( 1024.0 * 1024.0 )
397+ ) ;
398+ tasks_inflight += 1 ;
399+ task_tx. send ( task) . unwrap ( ) ;
400+ continue ;
400401 }
401402 }
402403
403- if let Some ( vec_idx) = launched_idx {
404- let task = pending. remove ( vec_idx) ;
405- tasks_inflight += 1 ;
406- task_tx. send ( task) . unwrap ( ) ;
407- } else if tasks_inflight > 0 {
408- // C: Block wait for completion when nothing fits
409- tracing:: info!(
410- "[scheduler] Pool full, waiting for task completion... pool_booked={:.2}MB, inflight={}" ,
411- pool. get_booked_total( ) as f64 / ( 1024.0 * 1024.0 ) ,
412- tasks_inflight
413- ) ;
414- if let Ok ( msg) = done_rx. recv ( ) {
404+ // No task launched: either nothing fits (so wait) or we are deadlocked.
405+ if tasks_inflight == 0 {
406+ tracing:: error!( "Deadlock: Remaining tasks are too big for the memory pool!" ) ;
407+ return Err ( ZKVMError :: BackendError ( BackendError :: CircuitError (
408+ "Deadlock: Remaining tasks are too big for the memory pool!"
409+ . to_string ( )
410+ . into_boxed_str ( ) ,
411+ ) ) ) ;
412+ }
413+
414+ tracing:: info!(
415+ "[scheduler] Pool full, waiting for task completion... pool_booked={:.2}MB, inflight={}" ,
416+ pool. get_booked_total( ) as f64 / ( 1024.0 * 1024.0 ) ,
417+ tasks_inflight
418+ ) ;
419+
420+ // Second call site blocks instead of busy-waiting when the pool is full; this
421+ // waits for the next completion to free memory before trying to launch again.
422+ match done_rx. recv ( ) {
423+ Ok ( msg) => {
415424 if let Err ( e) =
416425 handle_completion ( msg, pool, & mut tasks_inflight, " (blocked)" )
417426 {
418427 drop ( task_tx) ;
419428 return Err ( e) ;
420429 }
421430 }
422- } else {
423- tracing:: error!( "Deadlock: Remaining tasks are too big for the memory pool!" ) ;
424- return Err ( ZKVMError :: BackendError ( BackendError :: CircuitError (
425- "Deadlock: Remaining tasks are too big for the memory pool!"
426- . to_string ( )
427- . into_boxed_str ( ) ,
428- ) ) ) ;
431+ Err ( _) => break ,
429432 }
430433 }
431434
0 commit comments