@@ -65,6 +65,8 @@ enum CleanupJob {
65
65
66
66
pub impl Scheduler {
67
67
68
+ fn in_task_context ( & self ) -> bool { self . current_task . is_some ( ) }
69
+
68
70
fn new ( event_loop : ~EventLoopObject ) -> Scheduler {
69
71
70
72
// Lazily initialize the global state, currently the scheduler TLS key
@@ -131,6 +133,59 @@ pub impl Scheduler {
131
133
}
132
134
}
133
135
136
+ // * Task-context operations
137
+
138
+ /// Called by a running task to end execution, after which it will
139
+ /// be recycled by the scheduler for reuse in a new task.
140
+ fn terminate_current_task ( ~self ) {
141
+ let mut self = self ;
142
+ assert ! ( self . in_task_context( ) ) ;
143
+
144
+ rtdebug ! ( "ending running task" ) ;
145
+
146
+ let dead_task = self . current_task . swap_unwrap ( ) ;
147
+ self . enqueue_cleanup_job ( RecycleTask ( dead_task) ) ;
148
+
149
+ local_sched:: put ( self ) ;
150
+
151
+ let sched = unsafe { local_sched:: unsafe_borrow ( ) } ;
152
+ let ( sched_context, last_task_context, _) = sched. get_contexts ( ) ;
153
+ let last_task_context = last_task_context. unwrap ( ) ;
154
+ Context :: swap ( last_task_context, sched_context) ;
155
+
156
+ // Control never reaches here
157
+ }
158
+
159
+ /// Switch directly to another task, without going through the scheduler.
160
+ /// You would want to think hard about doing this, e.g. if there are
161
+ /// pending I/O events it would be a bad idea.
162
+ fn resume_task_from_running_task_direct ( ~self , next_task : ~Task ) {
163
+ let mut self = self ;
164
+ assert ! ( self . in_task_context( ) ) ;
165
+
166
+ rtdebug ! ( "switching tasks" ) ;
167
+
168
+ let old_running_task = self . current_task . swap_unwrap ( ) ;
169
+ self . enqueue_cleanup_job ( RescheduleTask ( old_running_task) ) ;
170
+ self . current_task = Some ( next_task) ;
171
+
172
+ local_sched:: put ( self ) ;
173
+
174
+ unsafe {
175
+ let sched = local_sched:: unsafe_borrow ( ) ;
176
+ let ( _, last_task_context, next_task_context) = sched. get_contexts ( ) ;
177
+ let last_task_context = last_task_context. unwrap ( ) ;
178
+ let next_task_context = next_task_context. unwrap ( ) ;
179
+ Context :: swap ( last_task_context, next_task_context) ;
180
+
181
+ // We could be executing in a different thread now
182
+ let sched = local_sched:: unsafe_borrow ( ) ;
183
+ sched. run_cleanup_job ( ) ;
184
+ }
185
+ }
186
+
187
+ // Core scheduling ops
188
+
134
189
fn resume_task_immediately ( ~self , task : ~Task ) {
135
190
let mut self = self ;
136
191
assert ! ( !self . in_task_context( ) ) ;
@@ -161,30 +216,6 @@ pub impl Scheduler {
161
216
}
162
217
}
163
218
164
-
165
- // * Task-context operations
166
-
167
- /// Called by a running task to end execution, after which it will
168
- /// be recycled by the scheduler for reuse in a new task.
169
- fn terminate_current_task ( ~self ) {
170
- let mut self = self ;
171
- assert ! ( self . in_task_context( ) ) ;
172
-
173
- rtdebug ! ( "ending running task" ) ;
174
-
175
- let dead_task = self . current_task . swap_unwrap ( ) ;
176
- self . enqueue_cleanup_job ( RecycleTask ( dead_task) ) ;
177
-
178
- local_sched:: put ( self ) ;
179
-
180
- let sched = unsafe { local_sched:: unsafe_borrow ( ) } ;
181
- let ( sched_context, last_task_context, _) = sched. get_contexts ( ) ;
182
- let last_task_context = last_task_context. unwrap ( ) ;
183
- Context :: swap ( last_task_context, sched_context) ;
184
-
185
- // Control never reaches here
186
- }
187
-
188
219
/// Block a running task, context switch to the scheduler, then pass the
189
220
/// blocked task to a closure.
190
221
///
@@ -219,14 +250,16 @@ pub impl Scheduler {
219
250
/// Switch directly to another task, without going through the scheduler.
220
251
/// You would want to think hard about doing this, e.g. if there are
221
252
/// pending I/O events it would be a bad idea.
222
- fn resume_task_from_running_task_direct ( ~self , next_task : ~Task ) {
253
+ fn switch_running_tasks_and_then ( ~self , next_task : ~Task , f : & fn ( ~ Task ) ) {
223
254
let mut self = self ;
224
255
assert ! ( self . in_task_context( ) ) ;
225
256
226
257
rtdebug ! ( "switching tasks" ) ;
227
258
228
259
let old_running_task = self . current_task . swap_unwrap ( ) ;
229
- self . enqueue_cleanup_job ( RescheduleTask ( old_running_task) ) ;
260
+ let f_fake_region = unsafe { transmute :: < & fn ( ~Task ) , & fn ( ~Task ) > ( f) } ;
261
+ let f_opaque = ClosureConverter :: from_fn ( f_fake_region) ;
262
+ self . enqueue_cleanup_job ( GiveTask ( old_running_task, f_opaque) ) ;
230
263
self . current_task = Some ( next_task) ;
231
264
232
265
local_sched:: put ( self ) ;
@@ -244,9 +277,9 @@ pub impl Scheduler {
244
277
}
245
278
}
246
279
247
- // * Other stuff
248
280
249
- fn in_task_context ( & self ) -> bool { self . current_task . is_some ( ) }
281
+
282
+ // * Other stuff
250
283
251
284
fn enqueue_cleanup_job ( & mut self , job : CleanupJob ) {
252
285
assert ! ( self . cleanup_job. is_none( ) ) ;
@@ -422,6 +455,34 @@ fn test_swap_tasks() {
422
455
}
423
456
}
424
457
458
+ #[ test]
459
+ fn test_swap_tasks_then ( ) {
460
+ do run_in_bare_thread {
461
+ let mut count = 0 ;
462
+ let count_ptr: * mut int = & mut count;
463
+
464
+ let mut sched = ~UvEventLoop :: new_scheduler ( ) ;
465
+ let task1 = ~do Task :: new ( & mut sched. stack_pool ) {
466
+ unsafe { * count_ptr = * count_ptr + 1 ; }
467
+ let mut sched = local_sched:: take ( ) ;
468
+ let task2 = ~do Task :: new ( & mut sched. stack_pool ) {
469
+ unsafe { * count_ptr = * count_ptr + 1 ; }
470
+ } ;
471
+ // Context switch directly to the new task
472
+ do sched. switch_running_tasks_and_then ( task2) |task1| {
473
+ let task1 = Cell ( task1) ;
474
+ do local_sched:: borrow |sched| {
475
+ sched. task_queue . push_front ( task1. take ( ) ) ;
476
+ }
477
+ }
478
+ unsafe { * count_ptr = * count_ptr + 1 ; }
479
+ } ;
480
+ sched. task_queue . push_back ( task1) ;
481
+ sched. run ( ) ;
482
+ assert ! ( count == 3 ) ;
483
+ }
484
+ }
485
+
425
486
#[ bench] #[ test] #[ ignore( reason = "long test" ) ]
426
487
fn test_run_a_lot_of_tasks_queued ( ) {
427
488
do run_in_bare_thread {
0 commit comments