@@ -252,12 +252,23 @@ impl Scheduler {
252
252
253
253
// * Execution Functions - Core Loop Logic
254
254
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).
261
272
fn run_sched_once ( mut ~self , stask : ~GreenTask ) {
262
273
// Make sure that we're not lying in that the `stask` argument is indeed
263
274
// the scheduler task for this scheduler.
@@ -269,26 +280,46 @@ impl Scheduler {
269
280
270
281
// First we check for scheduler messages, these are higher
271
282
// 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 ) ;
284
285
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
+ }
291
305
} ;
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
+ }
292
323
293
324
// If we got here then there was no work to do.
294
325
// Generate a SchedHandle and push it to the sleeper list so
@@ -318,7 +349,7 @@ impl Scheduler {
318
349
// return None.
319
350
fn interpret_message_queue ( mut ~self , stask : ~GreenTask ,
320
351
effort : EffortLevel )
321
- -> Option < ( ~Scheduler , ~GreenTask ) >
352
+ -> ( ~Scheduler , ~GreenTask , bool )
322
353
{
323
354
324
355
let msg = if effort == DontTryTooHard {
@@ -349,25 +380,25 @@ impl Scheduler {
349
380
Some ( PinnedTask ( task) ) => {
350
381
let mut task = task;
351
382
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 )
354
385
}
355
386
Some ( TaskFromFriend ( task) ) => {
356
387
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 )
360
392
}
361
393
Some ( RunOnce ( task) ) => {
362
394
// bypass the process_task logic to force running this task once
363
395
// 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 )
366
398
}
367
399
Some ( Wake ) => {
368
400
self . sleepy = false ;
369
- stask. put_with_sched ( self ) ;
370
- return None ;
401
+ ( self , stask, true )
371
402
}
372
403
Some ( Shutdown ) => {
373
404
rtdebug ! ( "shutting down" ) ;
@@ -389,31 +420,30 @@ impl Scheduler {
389
420
// event loop references we will shut down.
390
421
self . no_sleep = true ;
391
422
self . sleepy = false ;
392
- stask. put_with_sched ( self ) ;
393
- return None ;
423
+ ( self , stask, true )
394
424
}
395
425
Some ( NewNeighbor ( neighbor) ) => {
396
426
self . work_queues . push ( neighbor) ;
397
- return Some ( ( self , stask) ) ;
398
- }
399
- None => {
400
- return Some ( ( self , stask) ) ;
427
+ ( self , stask, false )
401
428
}
429
+ None => ( self , stask, false )
402
430
}
403
431
}
404
432
405
- fn do_work ( mut ~self , stask : ~GreenTask ) -> Option < ( ~Scheduler , ~GreenTask ) > {
433
+ fn do_work ( mut ~self ,
434
+ stask : ~GreenTask ) -> ( ~Scheduler , ~GreenTask , bool ) {
406
435
rtdebug ! ( "scheduler calling do work" ) ;
407
436
match self . find_work ( ) {
408
437
Some ( task) => {
409
438
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 )
413
443
}
414
444
None => {
415
445
rtdebug ! ( "no work was found, returning the scheduler struct" ) ;
416
- return Some ( ( self , stask) ) ;
446
+ ( self , stask, false )
417
447
}
418
448
}
419
449
}
@@ -486,7 +516,8 @@ impl Scheduler {
486
516
// place.
487
517
488
518
fn process_task ( mut ~self , cur : ~GreenTask ,
489
- mut next : ~GreenTask , schedule_fn : SchedulingFn ) {
519
+ mut next : ~GreenTask ,
520
+ schedule_fn : SchedulingFn ) -> ( ~Scheduler , ~GreenTask ) {
490
521
rtdebug ! ( "processing a task" ) ;
491
522
492
523
match next. take_unwrap_home ( ) {
@@ -495,23 +526,23 @@ impl Scheduler {
495
526
rtdebug ! ( "sending task home" ) ;
496
527
next. give_home ( HomeSched ( home_handle) ) ;
497
528
Scheduler :: send_task_home ( next) ;
498
- cur . put_with_sched ( self ) ;
529
+ ( self , cur )
499
530
} else {
500
531
rtdebug ! ( "running task here" ) ;
501
532
next. give_home ( HomeSched ( home_handle) ) ;
502
- schedule_fn ( self , cur, next) ;
533
+ schedule_fn ( self , cur, next)
503
534
}
504
535
}
505
536
AnySched if self . run_anything => {
506
537
rtdebug ! ( "running anysched task here" ) ;
507
538
next. give_home ( AnySched ) ;
508
- schedule_fn ( self , cur, next) ;
539
+ schedule_fn ( self , cur, next)
509
540
}
510
541
AnySched => {
511
542
rtdebug ! ( "sending task to friend" ) ;
512
543
next. give_home ( AnySched ) ;
513
544
self . send_to_friend ( next) ;
514
- cur . put_with_sched ( self ) ;
545
+ ( self , cur )
515
546
}
516
547
}
517
548
}
@@ -664,18 +695,19 @@ impl Scheduler {
664
695
// * Context Swapping Helpers - Here be ugliness!
665
696
666
697
pub fn resume_task_immediately ( ~self , cur : ~GreenTask ,
667
- next : ~GreenTask ) -> ~ GreenTask {
698
+ next : ~GreenTask ) -> ( ~ Scheduler , ~ GreenTask ) {
668
699
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| {
670
701
assert ! ( sched. sched_task. is_none( ) ) ;
671
702
sched. sched_task = Some ( stask) ;
672
- } )
703
+ } ) ;
704
+ ( cur. sched . take_unwrap ( ) , cur)
673
705
}
674
706
675
707
fn resume_task_immediately_cl ( sched : ~Scheduler ,
676
708
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)
679
711
}
680
712
681
713
/// Block a running task, context switch to the scheduler, then pass the
@@ -741,15 +773,17 @@ impl Scheduler {
741
773
cur. put ( ) ;
742
774
}
743
775
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| {
746
779
if last_task. is_sched ( ) {
747
780
assert ! ( sched. sched_task. is_none( ) ) ;
748
781
sched. sched_task = Some ( last_task) ;
749
782
} else {
750
783
sched. enqueue_task ( last_task) ;
751
784
}
752
- } ) . put ( )
785
+ } ) ;
786
+ ( cur. sched . take_unwrap ( ) , cur)
753
787
}
754
788
755
789
// * Task Context Helpers
@@ -769,7 +803,9 @@ impl Scheduler {
769
803
}
770
804
771
805
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) ;
773
809
}
774
810
775
811
pub fn run_task_later ( mut cur : ~GreenTask , next : ~GreenTask ) {
@@ -836,7 +872,8 @@ impl Scheduler {
836
872
837
873
// Supporting types
838
874
839
- type SchedulingFn = extern "Rust" fn ( ~Scheduler , ~GreenTask , ~GreenTask ) ;
875
+ type SchedulingFn = fn ( ~Scheduler , ~GreenTask , ~GreenTask )
876
+ -> ( ~Scheduler , ~GreenTask ) ;
840
877
841
878
pub enum SchedMessage {
842
879
Wake ,
0 commit comments