@@ -252,12 +252,23 @@ impl Scheduler {
252252
253253 // * Execution Functions - Core Loop Logic
254254
255- // The model for this function is that you continue through it
256- // until you either use the scheduler while performing a schedule
257- // action, in which case you give it away and return early, or
258- // you reach the end and sleep. In the case that a scheduler
259- // action is performed the loop is evented such that this function
260- // is called again.
255+ // This function is run from the idle callback on the uv loop, indicating
256+ // that there are no I/O events pending. When this function returns, we will
257+ // fall back to epoll() in the uv event loop, waiting for more things to
258+ // happen. We may come right back off epoll() if the idle callback is still
259+ // active, in which case we're truly just polling to see if I/O events are
260+ // complete.
261+ //
262+ // The model for this function is to execute as much work as possible while
263+ // still fairly considering I/O tasks. Falling back to epoll() frequently is
264+ // often quite expensive, so we attempt to avoid it as much as possible. If
265+ // we have any active I/O on the event loop, then we're forced to fall back
266+ // to epoll() in order to provide fairness, but as long as we're doing work
267+ // and there's no active I/O, we can continue to do work.
268+ //
269+ // If we try really hard to do some work, but no work is available to be
270+ // done, then we fall back to epoll() to block this thread waiting for more
271+ // work (instead of busy waiting).
261272 fn run_sched_once ( mut ~self , stask : ~GreenTask ) {
262273 // Make sure that we're not lying in that the `stask` argument is indeed
263274 // the scheduler task for this scheduler.
@@ -269,26 +280,46 @@ impl Scheduler {
269280
270281 // First we check for scheduler messages, these are higher
271282 // priority than regular tasks.
272- let ( sched, stask) =
273- match self . interpret_message_queue ( stask, DontTryTooHard ) {
274- Some ( pair) => pair,
275- None => return
276- } ;
277-
278- // This helper will use a randomized work-stealing algorithm
279- // to find work.
280- let ( sched, stask) = match sched. do_work ( stask) {
281- Some ( pair) => pair,
282- None => return
283- } ;
283+ let ( mut sched, mut stask, mut did_work) =
284+ self . interpret_message_queue ( stask, DontTryTooHard ) ;
284285
285- // Now, before sleeping we need to find out if there really
286- // were any messages. Give it your best!
287- let ( mut sched, stask) =
288- match sched. interpret_message_queue ( stask, GiveItYourBest ) {
289- Some ( pair) => pair,
290- None => return
286+ // After processing a message, we consider doing some more work on the
287+ // event loop. The "keep going" condition changes after the first
288+ // iteration becase we don't want to spin here infinitely.
289+ //
290+ // Once we start doing work we can keep doing work so long as the
291+ // iteration does something. Note that we don't want to starve the
292+ // message queue here, so each iteration when we're done working we
293+ // check the message queue regardless of whether we did work or not.
294+ let mut keep_going = !did_work || !sched. event_loop . has_active_io ( ) ;
295+ while keep_going {
296+ let ( a, b, c) = match sched. do_work ( stask) {
297+ ( sched, task, false ) => {
298+ sched. interpret_message_queue ( task, GiveItYourBest )
299+ }
300+ ( sched, task, true ) => {
301+ let ( sched, task, _) =
302+ sched. interpret_message_queue ( task, GiveItYourBest ) ;
303+ ( sched, task, true )
304+ }
291305 } ;
306+ sched = a;
307+ stask = b;
308+ did_work = c;
309+
310+ // We only keep going if we managed to do something productive and
311+ // also don't have any active I/O. If we didn't do anything, we
312+ // should consider going to sleep, and if we have active I/O we need
313+ // to poll for completion.
314+ keep_going = did_work && !sched. event_loop . has_active_io ( ) ;
315+ }
316+
317+ // If we ever did some work, then we shouldn't put our scheduler
318+ // entirely to sleep just yet. Leave the idle callback active and fall
319+ // back to epoll() to see what's going on.
320+ if did_work {
321+ return stask. put_with_sched ( sched) ;
322+ }
292323
293324 // If we got here then there was no work to do.
294325 // Generate a SchedHandle and push it to the sleeper list so
@@ -318,7 +349,7 @@ impl Scheduler {
318349 // return None.
319350 fn interpret_message_queue ( mut ~self , stask : ~GreenTask ,
320351 effort : EffortLevel )
321- -> Option < ( ~Scheduler , ~GreenTask ) >
352+ -> ( ~Scheduler , ~GreenTask , bool )
322353 {
323354
324355 let msg = if effort == DontTryTooHard {
@@ -349,25 +380,25 @@ impl Scheduler {
349380 Some ( PinnedTask ( task) ) => {
350381 let mut task = task;
351382 task. give_home ( HomeSched ( self . make_handle ( ) ) ) ;
352- self . resume_task_immediately ( stask, task) . put ( ) ;
353- return None ;
383+ let ( sched , task ) = self . resume_task_immediately ( stask, task) ;
384+ ( sched , task , true )
354385 }
355386 Some ( TaskFromFriend ( task) ) => {
356387 rtdebug ! ( "got a task from a friend. lovely!" ) ;
357- self . process_task ( stask, task,
358- Scheduler :: resume_task_immediately_cl) ;
359- return None ;
388+ let ( sched, task) =
389+ self . process_task ( stask, task,
390+ Scheduler :: resume_task_immediately_cl) ;
391+ ( sched, task, true )
360392 }
361393 Some ( RunOnce ( task) ) => {
362394 // bypass the process_task logic to force running this task once
363395 // on this home scheduler. This is often used for I/O (homing).
364- self . resume_task_immediately ( stask, task) . put ( ) ;
365- return None ;
396+ let ( sched , task ) = self . resume_task_immediately ( stask, task) ;
397+ ( sched , task , true )
366398 }
367399 Some ( Wake ) => {
368400 self . sleepy = false ;
369- stask. put_with_sched ( self ) ;
370- return None ;
401+ ( self , stask, true )
371402 }
372403 Some ( Shutdown ) => {
373404 rtdebug ! ( "shutting down" ) ;
@@ -389,31 +420,30 @@ impl Scheduler {
389420 // event loop references we will shut down.
390421 self . no_sleep = true ;
391422 self . sleepy = false ;
392- stask. put_with_sched ( self ) ;
393- return None ;
423+ ( self , stask, true )
394424 }
395425 Some ( NewNeighbor ( neighbor) ) => {
396426 self . work_queues . push ( neighbor) ;
397- return Some ( ( self , stask) ) ;
398- }
399- None => {
400- return Some ( ( self , stask) ) ;
427+ ( self , stask, false )
401428 }
429+ None => ( self , stask, false )
402430 }
403431 }
404432
405- fn do_work ( mut ~self , stask : ~GreenTask ) -> Option < ( ~Scheduler , ~GreenTask ) > {
433+ fn do_work ( mut ~self ,
434+ stask : ~GreenTask ) -> ( ~Scheduler , ~GreenTask , bool ) {
406435 rtdebug ! ( "scheduler calling do work" ) ;
407436 match self . find_work ( ) {
408437 Some ( task) => {
409438 rtdebug ! ( "found some work! running the task" ) ;
410- self . process_task ( stask, task,
411- Scheduler :: resume_task_immediately_cl) ;
412- return None ;
439+ let ( sched, task) =
440+ self . process_task ( stask, task,
441+ Scheduler :: resume_task_immediately_cl) ;
442+ ( sched, task, true )
413443 }
414444 None => {
415445 rtdebug ! ( "no work was found, returning the scheduler struct" ) ;
416- return Some ( ( self , stask) ) ;
446+ ( self , stask, false )
417447 }
418448 }
419449 }
@@ -486,7 +516,8 @@ impl Scheduler {
486516 // place.
487517
488518 fn process_task ( mut ~self , cur : ~GreenTask ,
489- mut next : ~GreenTask , schedule_fn : SchedulingFn ) {
519+ mut next : ~GreenTask ,
520+ schedule_fn : SchedulingFn ) -> ( ~Scheduler , ~GreenTask ) {
490521 rtdebug ! ( "processing a task" ) ;
491522
492523 match next. take_unwrap_home ( ) {
@@ -495,23 +526,23 @@ impl Scheduler {
495526 rtdebug ! ( "sending task home" ) ;
496527 next. give_home ( HomeSched ( home_handle) ) ;
497528 Scheduler :: send_task_home ( next) ;
498- cur . put_with_sched ( self ) ;
529+ ( self , cur )
499530 } else {
500531 rtdebug ! ( "running task here" ) ;
501532 next. give_home ( HomeSched ( home_handle) ) ;
502- schedule_fn ( self , cur, next) ;
533+ schedule_fn ( self , cur, next)
503534 }
504535 }
505536 AnySched if self . run_anything => {
506537 rtdebug ! ( "running anysched task here" ) ;
507538 next. give_home ( AnySched ) ;
508- schedule_fn ( self , cur, next) ;
539+ schedule_fn ( self , cur, next)
509540 }
510541 AnySched => {
511542 rtdebug ! ( "sending task to friend" ) ;
512543 next. give_home ( AnySched ) ;
513544 self . send_to_friend ( next) ;
514- cur . put_with_sched ( self ) ;
545+ ( self , cur )
515546 }
516547 }
517548 }
@@ -664,18 +695,19 @@ impl Scheduler {
664695 // * Context Swapping Helpers - Here be ugliness!
665696
666697 pub fn resume_task_immediately ( ~self , cur : ~GreenTask ,
667- next : ~GreenTask ) -> ~ GreenTask {
698+ next : ~GreenTask ) -> ( ~ Scheduler , ~ GreenTask ) {
668699 assert ! ( cur. is_sched( ) ) ;
669- self . change_task_context ( cur, next, |sched, stask| {
700+ let mut cur = self . change_task_context ( cur, next, |sched, stask| {
670701 assert ! ( sched. sched_task. is_none( ) ) ;
671702 sched. sched_task = Some ( stask) ;
672- } )
703+ } ) ;
704+ ( cur. sched . take_unwrap ( ) , cur)
673705 }
674706
675707 fn resume_task_immediately_cl ( sched : ~Scheduler ,
676708 cur : ~GreenTask ,
677- next : ~GreenTask ) {
678- sched. resume_task_immediately ( cur, next) . put ( )
709+ next : ~GreenTask ) -> ( ~ Scheduler , ~ GreenTask ) {
710+ sched. resume_task_immediately ( cur, next)
679711 }
680712
681713 /// Block a running task, context switch to the scheduler, then pass the
@@ -741,15 +773,17 @@ impl Scheduler {
741773 cur. put ( ) ;
742774 }
743775
744- fn switch_task ( sched : ~Scheduler , cur : ~GreenTask , next : ~GreenTask ) {
745- sched. change_task_context ( cur, next, |sched, last_task| {
776+ fn switch_task ( sched : ~Scheduler , cur : ~GreenTask ,
777+ next : ~GreenTask ) -> ( ~Scheduler , ~GreenTask ) {
778+ let mut cur = sched. change_task_context ( cur, next, |sched, last_task| {
746779 if last_task. is_sched ( ) {
747780 assert ! ( sched. sched_task. is_none( ) ) ;
748781 sched. sched_task = Some ( last_task) ;
749782 } else {
750783 sched. enqueue_task ( last_task) ;
751784 }
752- } ) . put ( )
785+ } ) ;
786+ ( cur. sched . take_unwrap ( ) , cur)
753787 }
754788
755789 // * Task Context Helpers
@@ -769,7 +803,9 @@ impl Scheduler {
769803 }
770804
771805 pub fn run_task ( ~self , cur : ~GreenTask , next : ~GreenTask ) {
772- self . process_task ( cur, next, Scheduler :: switch_task) ;
806+ let ( sched, task) =
807+ self . process_task ( cur, next, Scheduler :: switch_task) ;
808+ task. put_with_sched ( sched) ;
773809 }
774810
775811 pub fn run_task_later ( mut cur : ~GreenTask , next : ~GreenTask ) {
@@ -836,7 +872,8 @@ impl Scheduler {
836872
837873// Supporting types
838874
839- type SchedulingFn = extern "Rust" fn ( ~Scheduler , ~GreenTask , ~GreenTask ) ;
875+ type SchedulingFn = fn ( ~Scheduler , ~GreenTask , ~GreenTask )
876+ -> ( ~Scheduler , ~GreenTask ) ;
840877
841878pub enum SchedMessage {
842879 Wake ,
0 commit comments