@@ -56,6 +56,7 @@ use crate::transfer::{BoxTransfer, IoRequest, PollWork, TransferId, WorkOutcome}
5656use crate :: metrics:: { IOCounters , IoSample } ;
5757use crate :: runtime:: ExecutionRuntime ;
5858
59+ use crate :: runtime:: sync:: { Submission , SubmissionQueue } ;
5960use crate :: runtime:: ScheduledWork ;
6061use crate :: scheduler:: descriptor:: TransferDescriptor ;
6162use crate :: scheduler:: ready_set:: ReadySet ;
@@ -66,6 +67,9 @@ use std::sync::Arc;
6667use std:: sync:: { OnceLock , RwLock } ;
6768use std:: time:: Duration ;
6869
70+ /// Batch size for work generated and submitted in a single round
71+ const SUBMISSION_QUEUE_SIZE : usize = 64 ;
72+
6973/// Event-driven scheduler for coordinating transfer work.
7074///
7175/// Clone is cheap (Arc).
@@ -79,6 +83,7 @@ struct SchedulerInner {
7983 io_counters : Arc < IOCounters > ,
8084 runtime : OnceLock < Arc < dyn ExecutionRuntime > > ,
8185 dispatched : AtomicUsize ,
86+ submission_queue : SubmissionQueue < ScheduledWork > ,
8287}
8388
8489impl std:: fmt:: Debug for Scheduler {
@@ -115,6 +120,7 @@ impl SchedulerBuilder {
115120 io_counters : self . io_counters ,
116121 runtime : OnceLock :: new ( ) ,
117122 dispatched : AtomicUsize :: new ( 0 ) ,
123+ submission_queue : SubmissionQueue :: new ( SUBMISSION_QUEUE_SIZE ) ,
118124 } ) ) ;
119125 let runtime = runtime_factory ( scheduler. clone ( ) ) ;
120126 scheduler
@@ -301,7 +307,7 @@ impl Scheduler {
301307 item : IoRequest { kind, data } ,
302308 descriptor : desc. clone ( ) ,
303309 } ;
304- self . dispatch_to_runtime ( next) ;
310+ self . dispatch_single ( next) ;
305311 }
306312
307313 // capacity has freed try to queue up more work
@@ -333,8 +339,20 @@ impl Scheduler {
333339 self . 0 . dispatched . load ( Ordering :: Relaxed ) < self . 0 . controller . target ( )
334340 }
335341
342+ /// Flush the current submission and re-enter for the next batch.
343+ fn submit_and_reenter < ' a > (
344+ & ' a self ,
345+ sub : Submission < ' a , ScheduledWork > ,
346+ ) -> Submission < ' a , ScheduledWork > {
347+ if let Some ( mut guard) = sub. submit ( ) {
348+ self . runtime ( ) . dispatch ( & mut guard) ;
349+ }
350+ self . 0 . submission_queue . enter ( )
351+ }
352+
336353 /// Generate work from ready transfers and dispatch to runtime.
337354 fn generate_work ( & self ) {
355+ let mut sub = self . 0 . submission_queue . enter ( ) ;
338356 while self . has_capacity ( ) {
339357 let Some ( desc) = self . 0 . ready_set . pop ( ) else {
340358 break ;
@@ -349,10 +367,16 @@ impl Scheduler {
349367 PollWork :: Ready ( item) => {
350368 desc. work_generated ( ) ;
351369 self . 0 . ready_set . insert ( desc. clone ( ) ) ;
352- self . dispatch_to_runtime ( ScheduledWork {
370+ self . 0 . dispatched . fetch_add ( 1 , Ordering :: Relaxed ) ;
371+ desc. work_queued ( ) ;
372+ let work = ScheduledWork {
353373 item,
354374 descriptor : desc,
355- } ) ;
375+ } ;
376+ if let Err ( work) = sub. push ( work) {
377+ sub = self . submit_and_reenter ( sub) ;
378+ sub. push ( work) . expect ( "empty queue after flush" ) ;
379+ }
356380 }
357381 PollWork :: Pending => {
358382 // re-added on wake as state machine progresses
@@ -363,12 +387,24 @@ impl Scheduler {
363387 }
364388 }
365389 }
390+ if let Some ( mut guard) = sub. submit ( ) {
391+ self . runtime ( ) . dispatch ( & mut guard) ;
392+ }
366393 }
367394
368- fn dispatch_to_runtime ( & self , work : ScheduledWork ) {
395+ fn dispatch_single ( & self , work : ScheduledWork ) {
369396 self . 0 . dispatched . fetch_add ( 1 , Ordering :: Relaxed ) ;
370397 work. descriptor . work_queued ( ) ;
371- self . runtime ( ) . dispatch ( work) ;
398+ let sub = self . 0 . submission_queue . enter ( ) ;
399+ if let Err ( work) = sub. push ( work) {
400+ let sub = self . submit_and_reenter ( sub) ;
401+ sub. push ( work) . expect ( "empty queue after flush" ) ;
402+ if let Some ( mut guard) = sub. submit ( ) {
403+ self . runtime ( ) . dispatch ( & mut guard) ;
404+ }
405+ } else if let Some ( mut guard) = sub. submit ( ) {
406+ self . runtime ( ) . dispatch ( & mut guard) ;
407+ }
372408 }
373409
374410 #[ allow( dead_code) ] // TODO: wire into Handle for graceful shutdown
0 commit comments