@@ -13,7 +13,6 @@ use option::{Option, Some, None};
13
13
use cast:: { transmute, transmute_mut_region, transmute_mut_unsafe} ;
14
14
use clone:: Clone ;
15
15
use unstable:: raw;
16
-
17
16
use super :: sleeper_list:: SleeperList ;
18
17
use super :: work_queue:: WorkQueue ;
19
18
use super :: stack:: { StackPool } ;
@@ -28,6 +27,9 @@ use rt::rtio::RemoteCallback;
28
27
use rt:: metrics:: SchedMetrics ;
29
28
use borrow:: { to_uint} ;
30
29
use cell:: Cell ;
30
+ use rand:: { XorShiftRng , RngUtil } ;
31
+ use iterator:: { range} ;
32
+ use vec:: { OwnedVector } ;
31
33
32
34
/// The Scheduler is responsible for coordinating execution of Coroutines
33
35
/// on a single thread. When the scheduler is running it is owned by
@@ -37,9 +39,11 @@ use cell::Cell;
37
39
/// XXX: This creates too many callbacks to run_sched_once, resulting
38
40
/// in too much allocation and too many events.
39
41
pub struct Scheduler {
40
- /// A queue of available work. Under a work-stealing policy there
41
- /// is one per Scheduler.
42
- work_queue : WorkQueue < ~Task > ,
42
+ /// There are N work queues, one per scheduler.
43
+ priv work_queue : WorkQueue < ~Task > ,
44
+ /// Work queues for the other schedulers. These are created by
45
+ /// cloning the core work queues.
46
+ work_queues : ~[ WorkQueue < ~Task > ] ,
43
47
/// The queue of incoming messages from other schedulers.
44
48
/// These are enqueued by SchedHandles after which a remote callback
45
49
/// is triggered to handle the message.
@@ -70,7 +74,10 @@ pub struct Scheduler {
70
74
run_anything : bool ,
71
75
/// If the scheduler shouldn't run some tasks, a friend to send
72
76
/// them to.
73
- friend_handle : Option < SchedHandle >
77
+ friend_handle : Option < SchedHandle > ,
78
+ /// A fast XorShift rng for scheduler use
79
+ rng : XorShiftRng
80
+
74
81
}
75
82
76
83
pub struct SchedHandle {
@@ -97,17 +104,21 @@ impl Scheduler {
97
104
98
105
pub fn new ( event_loop : ~EventLoopObject ,
99
106
work_queue : WorkQueue < ~Task > ,
107
+ work_queues : ~[ WorkQueue < ~Task > ] ,
100
108
sleeper_list : SleeperList )
101
109
-> Scheduler {
102
110
103
- Scheduler :: new_special ( event_loop, work_queue, sleeper_list, true , None )
111
+ Scheduler :: new_special ( event_loop, work_queue,
112
+ work_queues,
113
+ sleeper_list, true , None )
104
114
105
115
}
106
116
107
117
// When you create a scheduler it isn't yet "in" a task, so the
108
118
// task field is None.
109
119
pub fn new_special ( event_loop : ~EventLoopObject ,
110
120
work_queue : WorkQueue < ~Task > ,
121
+ work_queues : ~[ WorkQueue < ~Task > ] ,
111
122
sleeper_list : SleeperList ,
112
123
run_anything : bool ,
113
124
friend : Option < SchedHandle > )
@@ -120,12 +131,14 @@ impl Scheduler {
120
131
no_sleep : false ,
121
132
event_loop : event_loop,
122
133
work_queue : work_queue,
134
+ work_queues : work_queues,
123
135
stack_pool : StackPool :: new ( ) ,
124
136
sched_task : None ,
125
137
cleanup_job : None ,
126
138
metrics : SchedMetrics :: new ( ) ,
127
139
run_anything : run_anything,
128
- friend_handle : friend
140
+ friend_handle : friend,
141
+ rng : XorShiftRng :: new ( )
129
142
}
130
143
}
131
144
@@ -248,7 +261,7 @@ impl Scheduler {
248
261
249
262
// Second activity is to try resuming a task from the queue.
250
263
251
- let result = sched. resume_task_from_queue ( ) ;
264
+ let result = sched. do_work ( ) ;
252
265
let mut sched = match result {
253
266
Some ( sched) => {
254
267
// Failed to dequeue a task, so we return.
@@ -415,47 +428,98 @@ impl Scheduler {
415
428
}
416
429
}
417
430
418
- // Resume a task from the queue - but also take into account that
419
- // it might not belong here.
431
+ // Workstealing: In this iteration of the runtime each scheduler
432
+ // thread has a distinct work queue. When no work is available
433
+ // locally, make a few attempts to steal work from the queues of
434
+ // other scheduler threads. If a few steals fail we end up in the
435
+ // old "no work" path which is fine.
436
+
437
+ // First step in the process is to find a task. This function does
438
+ // that by first checking the local queue, and if there is no work
439
+ // there, trying to steal from the remote work queues.
440
+ fn find_work ( & mut self ) -> Option < ~Task > {
441
+ rtdebug ! ( "scheduler looking for work" ) ;
442
+ match self . work_queue . pop ( ) {
443
+ Some ( task) => {
444
+ rtdebug ! ( "found a task locally" ) ;
445
+ return Some ( task)
446
+ }
447
+ None => {
448
+ // Our naive stealing, try kinda hard.
449
+ rtdebug ! ( "scheduler trying to steal" ) ;
450
+ let _len = self . work_queues . len ( ) ;
451
+ return self . try_steals ( 2 ) ;
452
+ }
453
+ }
454
+ }
455
+
456
+ // With no backoff try stealing n times from the queues the
457
+ // scheduler knows about. This naive implementation can steal from
458
+ // our own queue or from other special schedulers.
459
+ fn try_steals ( & mut self , n : uint ) -> Option < ~Task > {
460
+ for _ in range ( 0 , n) {
461
+ let index = self . rng . gen_uint_range ( 0 , self . work_queues . len ( ) ) ;
462
+ let work_queues = & mut self . work_queues ;
463
+ match work_queues[ index] . steal ( ) {
464
+ Some ( task) => {
465
+ rtdebug ! ( "found task by stealing" ) ; return Some ( task)
466
+ }
467
+ None => ( )
468
+ }
469
+ } ;
470
+ rtdebug ! ( "giving up on stealing" ) ;
471
+ return None ;
472
+ }
420
473
421
- // If we perform a scheduler action we give away the scheduler ~
422
- // pointer, if it is still available we return it.
474
+ // Given a task, execute it correctly.
475
+ fn process_task ( ~self , task : ~Task ) -> Option < ~Scheduler > {
476
+ let mut this = self ;
477
+ let mut task = task;
423
478
424
- fn resume_task_from_queue ( ~ self ) -> Option < ~ Scheduler > {
479
+ rtdebug ! ( "processing a task" ) ;
425
480
481
+ let home = task. take_unwrap_home ( ) ;
482
+ match home {
483
+ Sched ( home_handle) => {
484
+ if home_handle. sched_id != this. sched_id ( ) {
485
+ rtdebug ! ( "sending task home" ) ;
486
+ task. give_home ( Sched ( home_handle) ) ;
487
+ Scheduler :: send_task_home ( task) ;
488
+ return Some ( this) ;
489
+ } else {
490
+ rtdebug ! ( "running task here" ) ;
491
+ task. give_home ( Sched ( home_handle) ) ;
492
+ this. resume_task_immediately ( task) ;
493
+ return None ;
494
+ }
495
+ }
496
+ AnySched if this. run_anything => {
497
+ rtdebug ! ( "running anysched task here" ) ;
498
+ task. give_home ( AnySched ) ;
499
+ this. resume_task_immediately ( task) ;
500
+ return None ;
501
+ }
502
+ AnySched => {
503
+ rtdebug ! ( "sending task to friend" ) ;
504
+ task. give_home ( AnySched ) ;
505
+ this. send_to_friend ( task) ;
506
+ return Some ( this) ;
507
+ }
508
+ }
509
+ }
510
+
511
+ // Bundle the helpers together.
512
+ fn do_work ( ~self ) -> Option < ~Scheduler > {
426
513
let mut this = self ;
427
514
428
- match this. work_queue . pop ( ) {
515
+ rtdebug ! ( "scheduler calling do work" ) ;
516
+ match this. find_work ( ) {
429
517
Some ( task) => {
430
- let mut task = task;
431
- let home = task. take_unwrap_home ( ) ;
432
- match home {
433
- Sched ( home_handle) => {
434
- if home_handle. sched_id != this. sched_id ( ) {
435
- task. give_home ( Sched ( home_handle) ) ;
436
- Scheduler :: send_task_home ( task) ;
437
- return Some ( this) ;
438
- } else {
439
- this. event_loop . callback ( Scheduler :: run_sched_once) ;
440
- task. give_home ( Sched ( home_handle) ) ;
441
- this. resume_task_immediately ( task) ;
442
- return None ;
443
- }
444
- }
445
- AnySched if this. run_anything => {
446
- this. event_loop . callback ( Scheduler :: run_sched_once) ;
447
- task. give_home ( AnySched ) ;
448
- this. resume_task_immediately ( task) ;
449
- return None ;
450
- }
451
- AnySched => {
452
- task. give_home ( AnySched ) ;
453
- this. send_to_friend ( task) ;
454
- return Some ( this) ;
455
- }
456
- }
518
+ rtdebug ! ( "found some work! processing the task" ) ;
519
+ return this. process_task ( task) ;
457
520
}
458
521
None => {
522
+ rtdebug ! ( "no work was found, returning the scheduler struct" ) ;
459
523
return Some ( this) ;
460
524
}
461
525
}
@@ -711,7 +775,6 @@ impl Scheduler {
711
775
GiveTask ( task, f) => f. to_fn ( ) ( self , task)
712
776
}
713
777
}
714
-
715
778
}
716
779
717
780
// The cases for the below function.
@@ -745,6 +808,8 @@ impl ClosureConverter for UnsafeTaskReceiver {
745
808
746
809
#[ cfg( test) ]
747
810
mod test {
811
+ extern mod extra;
812
+
748
813
use prelude:: * ;
749
814
use rt:: test:: * ;
750
815
use unstable:: run_in_bare_thread;
@@ -862,12 +927,15 @@ mod test {
862
927
do run_in_bare_thread {
863
928
864
929
let sleepers = SleeperList :: new ( ) ;
865
- let work_queue = WorkQueue :: new ( ) ;
930
+ let normal_queue = WorkQueue :: new ( ) ;
931
+ let special_queue = WorkQueue :: new ( ) ;
932
+ let queues = ~[ normal_queue. clone ( ) , special_queue. clone ( ) ] ;
866
933
867
934
// Our normal scheduler
868
935
let mut normal_sched = ~Scheduler :: new (
869
936
~UvEventLoop :: new ( ) ,
870
- work_queue. clone ( ) ,
937
+ normal_queue,
938
+ queues. clone ( ) ,
871
939
sleepers. clone ( ) ) ;
872
940
873
941
let normal_handle = Cell :: new ( normal_sched. make_handle ( ) ) ;
@@ -877,7 +945,8 @@ mod test {
877
945
// Our special scheduler
878
946
let mut special_sched = ~Scheduler :: new_special (
879
947
~UvEventLoop :: new ( ) ,
880
- work_queue. clone ( ) ,
948
+ special_queue. clone ( ) ,
949
+ queues. clone ( ) ,
881
950
sleepers. clone ( ) ,
882
951
false ,
883
952
Some ( friend_handle) ) ;
0 commit comments