@@ -63,17 +63,19 @@ use cell::Cell;
63
63
use clone:: Clone ;
64
64
use container:: Container ;
65
65
use from_str:: FromStr ;
66
+ use iter:: Times ;
66
67
use iterator:: IteratorUtil ;
67
68
use option:: { Some , None } ;
68
69
use os;
69
70
use ptr:: RawPtr ;
70
- use uint;
71
71
use rt:: sched:: { Scheduler , Coroutine , Shutdown } ;
72
72
use rt:: sleeper_list:: SleeperList ;
73
73
use rt:: task:: Task ;
74
74
use rt:: thread:: Thread ;
75
75
use rt:: work_queue:: WorkQueue ;
76
76
use rt:: uv:: uvio:: UvEventLoop ;
77
+ use unstable:: atomics:: { AtomicInt , SeqCst } ;
78
+ use unstable:: sync:: UnsafeAtomicRcBox ;
77
79
use vec:: { OwnedVector , MutableVector } ;
78
80
79
81
/// The global (exchange) heap.
@@ -148,7 +150,7 @@ pub mod local_ptr;
148
150
/// Bindings to pthread/windows thread-local storage.
149
151
pub mod thread_local_storage;
150
152
151
- /// A concurrent data structure with which parent tasks wait on child tasks.
153
+ /// For waiting on child tasks.
152
154
pub mod join_latch;
153
155
154
156
pub mod metrics;
@@ -174,68 +176,95 @@ pub mod util;
174
176
pub fn start ( _argc : int , _argv : * * u8 , crate_map : * u8 , main : ~fn ( ) ) -> int {
175
177
176
178
init ( crate_map) ;
177
- run ( main) ;
179
+ let exit_code = run ( main) ;
178
180
cleanup ( ) ;
179
181
180
- return 0 ;
182
+ return exit_code ;
181
183
}
182
184
183
185
/// One-time runtime initialization. Currently all this does is set up logging
184
186
/// based on the RUST_LOG environment variable.
185
187
pub fn init ( crate_map : * u8 ) {
186
188
logging:: init ( crate_map) ;
189
+ unsafe { rust_update_gc_metadata ( crate_map) }
190
+
191
+ extern {
192
+ fn rust_update_gc_metadata ( crate_map : * u8 ) ;
193
+ }
187
194
}
188
195
196
+ /// One-time runtime cleanup.
189
197
pub fn cleanup ( ) {
190
198
global_heap:: cleanup ( ) ;
191
199
}
192
200
193
- pub fn run( main : ~fn ( ) ) {
201
+ /// Execute the main function in a scheduler.
202
+ ///
203
+ /// Configures the runtime according to the environment, by default
204
+ /// using a task scheduler with the same number of threads as cores.
205
+ /// Returns a process exit code.
206
+ pub fn run ( main : ~fn ( ) ) -> int {
207
+
208
+ static DEFAULT_ERROR_CODE : int = 101 ;
209
+
194
210
let nthreads = match os:: getenv ( "RUST_THREADS" ) {
195
211
Some ( nstr) => FromStr :: from_str ( nstr) . get ( ) ,
196
- None => unsafe {
197
- // Using more threads than cores in test code
198
- // to force the OS to preempt them frequently.
199
- // Assuming that this help stress test concurrent types.
200
- util:: num_cpus ( ) * 2
201
- }
212
+ None => unsafe { util:: num_cpus ( ) }
202
213
} ;
203
214
215
+ // The shared list of sleeping schedulers. Schedulers wake each other
216
+ // occassionally to do new work.
204
217
let sleepers = SleeperList :: new ( ) ;
218
+ // The shared work queue. Temporary until work stealing is implemented.
205
219
let work_queue = WorkQueue :: new ( ) ;
206
220
207
- let mut handles = ~ [ ] ;
221
+ // The schedulers.
208
222
let mut scheds = ~[ ] ;
223
+ // Handles to the schedulers. When the main task ends these will be
224
+ // sent the Shutdown message to terminate the schedulers.
225
+ let mut handles = ~[ ] ;
209
226
210
- for uint:: range( 0 , nthreads) |_| {
227
+ for nthreads . times {
228
+ // Every scheduler is driven by an I/O event loop.
211
229
let loop_ = ~UvEventLoop : : new( ) ;
212
230
let mut sched = ~Scheduler :: new( loop_, work_queue. clone( ) , sleepers. clone( ) ) ;
213
231
let handle = sched. make_handle( ) ;
214
232
215
- handles. push ( handle) ;
216
233
scheds. push( sched) ;
234
+ handles. push( handle) ;
217
235
}
218
236
219
- let main_cell = Cell :: new ( main) ;
237
+ // Create a shared cell for transmitting the process exit
238
+ // code from the main task to this function.
239
+ let exit_code = UnsafeAtomicRcBox :: new( AtomicInt :: new( 0 ) ) ;
240
+ let exit_code_clone = exit_code. clone( ) ;
241
+
242
+ // When the main task exits, after all the tasks in the main
243
+ // task tree, shut down the schedulers and set the exit code.
220
244
let handles = Cell :: new( handles) ;
221
- let mut new_task = ~Task :: new_root ( ) ;
222
- let on_exit: ~fn ( bool ) = |exit_status| {
245
+ let on_exit: ~fn ( bool) = |exit_success| {
223
246
224
247
let mut handles = handles. take( ) ;
225
- // Tell schedulers to exit
226
248
for handles. mut_iter( ) . advance |handle| {
227
249
handle. send( Shutdown ) ;
228
250
}
229
251
230
- rtassert ! ( exit_status) ;
252
+ unsafe {
253
+ let exit_code = if exit_success { 0 } else { DEFAULT_ERROR_CODE } ;
254
+ ( * exit_code_clone. get( ) ) . store( exit_code, SeqCst ) ;
255
+ }
231
256
} ;
257
+
258
+ // Create and enqueue the main task.
259
+ let main_cell = Cell :: new( main) ;
260
+ let mut new_task = ~Task :: new_root( ) ;
232
261
new_task. on_exit = Some ( on_exit) ;
233
262
let main_task = ~Coroutine :: with_task( & mut scheds[ 0 ] . stack_pool,
234
263
new_task, main_cell. take( ) ) ;
235
264
scheds[ 0 ] . enqueue_task( main_task) ;
236
265
266
+ // Run each scheduler in a thread.
237
267
let mut threads = ~[ ] ;
238
-
239
268
while !scheds. is_empty( ) {
240
269
let sched = scheds. pop( ) ;
241
270
let sched_cell = Cell :: new( sched) ;
@@ -248,7 +277,12 @@ pub fn run(main: ~fn()) {
248
277
}
249
278
250
279
// Wait for schedulers
251
- let _threads = threads;
280
+ { let _threads = threads; }
281
+
282
+ // Return the exit code
283
+ unsafe {
284
+ ( * exit_code. get( ) ) . load( SeqCst )
285
+ }
252
286
}
253
287
254
288
/// Possible contexts in which Rust code may be executing.
0 commit comments