Skip to content

Commit cab44fb

Browse files
committed
std: Introduce a Runtime trait
This trait is used to abstract the differences between 1:1 and M:N scheduling and is the sole dispatch point for the differences between these two scheduling modes. This, and the following series of commits, is not intended to compile. Only after the entire transition is complete are programs expected to compile.
1 parent 32e730f commit cab44fb

File tree

1 file changed

+25
-269
lines changed

1 file changed

+25
-269
lines changed

src/libstd/rt/mod.rs

Lines changed: 25 additions & 269 deletions
Original file line numberDiff line numberDiff line change
@@ -185,41 +185,33 @@ pub mod args;
185185
// Support for dynamic borrowck
186186
pub mod borrowck;
187187

188-
/// Set up a default runtime configuration, given compiler-supplied arguments.
189-
///
190-
/// This is invoked by the `start` _language item_ (unstable::lang) to
191-
/// run a Rust executable.
192-
///
193-
/// # Arguments
194-
///
195-
/// * `argc` & `argv` - The argument vector. On Unix this information is used
196-
/// by os::args.
197-
///
198-
/// # Return value
199-
///
200-
/// The return value is used as the process return code. 0 on success, 101 on error.
201-
pub fn start(argc: int, argv: **u8, main: proc()) -> int {
202-
203-
init(argc, argv);
204-
let exit_code = run(main);
205-
// unsafe is ok b/c we're sure that the runtime is gone
206-
unsafe { cleanup(); }
188+
/// The default error code of the rust runtime if the main task fails instead
189+
/// of exiting cleanly.
190+
pub static DEFAULT_ERROR_CODE: int = 101;
207191

208-
return exit_code;
209-
}
210-
211-
/// Like `start` but creates an additional scheduler on the current thread,
212-
/// which in most cases will be the 'main' thread, and pins the main task to it.
192+
/// The interface to the current runtime.
213193
///
214-
/// This is appropriate for running code that must execute on the main thread,
215-
/// such as the platform event loop and GUI.
216-
pub fn start_on_main_thread(argc: int, argv: **u8, main: proc()) -> int {
217-
init(argc, argv);
218-
let exit_code = run_on_main_thread(main);
219-
// unsafe is ok b/c we're sure that the runtime is gone
220-
unsafe { cleanup(); }
221-
222-
return exit_code;
194+
/// This trait is used as the abstraction between 1:1 and M:N scheduling. The
195+
/// two independent crates, libnative and libgreen, both have objects which
196+
/// implement this trait. The goal of this trait is to encompass all the
197+
/// fundamental differences in functionality between the 1:1 and M:N runtime
198+
/// modes.
199+
pub trait Runtime {
200+
// Necessary scheduling functions, used for channels and blocking I/O
201+
// (sometimes).
202+
fn yield_now(~self, cur_task: ~Task);
203+
fn maybe_yield(~self, cur_task: ~Task);
204+
fn deschedule(~self, times: uint, cur_task: ~Task,
205+
f: |BlockedTask| -> Result<(), BlockedTask>);
206+
fn reawaken(~self, to_wake: ~Task, can_resched: bool);
207+
208+
// Miscellaneous calls which are very different depending on what context
209+
// you're in.
210+
fn spawn_sibling(~self, cur_task: ~Task, opts: TaskOpts, f: proc());
211+
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>>;
212+
213+
// XXX: This is a serious code smell and this should not exist at all.
214+
fn wrap(~self) -> ~Any;
223215
}
224216

225217
/// One-time runtime initialization.
@@ -250,239 +242,3 @@ pub unsafe fn cleanup() {
250242
args::cleanup();
251243
local_ptr::cleanup();
252244
}
253-
254-
/// Execute the main function in a scheduler.
255-
///
256-
/// Configures the runtime according to the environment, by default
257-
/// using a task scheduler with the same number of threads as cores.
258-
/// Returns a process exit code.
259-
pub fn run(main: proc()) -> int {
260-
run_(main, false)
261-
}
262-
263-
pub fn run_on_main_thread(main: proc()) -> int {
264-
run_(main, true)
265-
}
266-
267-
fn run_(main: proc(), use_main_sched: bool) -> int {
268-
static DEFAULT_ERROR_CODE: int = 101;
269-
270-
let nscheds = util::default_sched_threads();
271-
272-
let mut main = Some(main);
273-
274-
// The shared list of sleeping schedulers.
275-
let sleepers = SleeperList::new();
276-
277-
// Create a work queue for each scheduler, ntimes. Create an extra
278-
// for the main thread if that flag is set. We won't steal from it.
279-
let mut pool = deque::BufferPool::new();
280-
let arr = vec::from_fn(nscheds, |_| pool.deque());
281-
let (workers, stealers) = vec::unzip(arr.move_iter());
282-
283-
// The schedulers.
284-
let mut scheds = ~[];
285-
// Handles to the schedulers. When the main task ends these will be
286-
// sent the Shutdown message to terminate the schedulers.
287-
let mut handles = ~[];
288-
289-
for worker in workers.move_iter() {
290-
rtdebug!("inserting a regular scheduler");
291-
292-
// Every scheduler is driven by an I/O event loop.
293-
let loop_ = new_event_loop();
294-
let mut sched = ~Scheduler::new(loop_,
295-
worker,
296-
stealers.clone(),
297-
sleepers.clone());
298-
let handle = sched.make_handle();
299-
300-
scheds.push(sched);
301-
handles.push(handle);
302-
}
303-
304-
// If we need a main-thread task then create a main thread scheduler
305-
// that will reject any task that isn't pinned to it
306-
let main_sched = if use_main_sched {
307-
308-
// Create a friend handle.
309-
let mut friend_sched = scheds.pop();
310-
let friend_handle = friend_sched.make_handle();
311-
scheds.push(friend_sched);
312-
313-
// This scheduler needs a queue that isn't part of the stealee
314-
// set.
315-
let (worker, _) = pool.deque();
316-
317-
let main_loop = new_event_loop();
318-
let mut main_sched = ~Scheduler::new_special(main_loop,
319-
worker,
320-
stealers.clone(),
321-
sleepers.clone(),
322-
false,
323-
Some(friend_handle));
324-
let mut main_handle = main_sched.make_handle();
325-
// Allow the scheduler to exit when the main task exits.
326-
// Note: sending the shutdown message also prevents the scheduler
327-
// from pushing itself to the sleeper list, which is used for
328-
// waking up schedulers for work stealing; since this is a
329-
// non-work-stealing scheduler it should not be adding itself
330-
// to the list.
331-
main_handle.send(Shutdown);
332-
Some(main_sched)
333-
} else {
334-
None
335-
};
336-
337-
// Create a shared cell for transmitting the process exit
338-
// code from the main task to this function.
339-
let exit_code = UnsafeArc::new(AtomicInt::new(0));
340-
let exit_code_clone = exit_code.clone();
341-
342-
// Used to sanity check that the runtime only exits once
343-
let exited_already = UnsafeArc::new(AtomicBool::new(false));
344-
345-
// When the main task exits, after all the tasks in the main
346-
// task tree, shut down the schedulers and set the exit code.
347-
let handles = handles;
348-
let on_exit: proc(TaskResult) = proc(exit_success) {
349-
unsafe {
350-
assert!(!(*exited_already.get()).swap(true, SeqCst),
351-
"the runtime already exited");
352-
}
353-
354-
let mut handles = handles;
355-
for handle in handles.mut_iter() {
356-
handle.send(Shutdown);
357-
}
358-
359-
unsafe {
360-
let exit_code = if exit_success.is_ok() {
361-
use rt::util;
362-
363-
// If we're exiting successfully, then return the global
364-
// exit status, which can be set programmatically.
365-
util::get_exit_status()
366-
} else {
367-
DEFAULT_ERROR_CODE
368-
};
369-
(*exit_code_clone.get()).store(exit_code, SeqCst);
370-
}
371-
};
372-
373-
let mut threads = ~[];
374-
let mut on_exit = Some(on_exit);
375-
376-
if !use_main_sched {
377-
378-
// In the case where we do not use a main_thread scheduler we
379-
// run the main task in one of our threads.
380-
381-
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool,
382-
None,
383-
::util::replace(&mut main,
384-
None).unwrap());
385-
main_task.name = Some(SendStrStatic("<main>"));
386-
main_task.death.on_exit = ::util::replace(&mut on_exit, None);
387-
388-
let sched = scheds.pop();
389-
let main_task = main_task;
390-
let thread = do Thread::start {
391-
sched.bootstrap(main_task);
392-
};
393-
threads.push(thread);
394-
}
395-
396-
// Run each remaining scheduler in a thread.
397-
for sched in scheds.move_rev_iter() {
398-
rtdebug!("creating regular schedulers");
399-
let thread = do Thread::start {
400-
let mut sched = sched;
401-
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool, None) || {
402-
rtdebug!("boostraping a non-primary scheduler");
403-
};
404-
sched.bootstrap(bootstrap_task);
405-
};
406-
threads.push(thread);
407-
}
408-
409-
// If we do have a main thread scheduler, run it now.
410-
411-
if use_main_sched {
412-
rtdebug!("about to create the main scheduler task");
413-
414-
let mut main_sched = main_sched.unwrap();
415-
416-
let home = Sched(main_sched.make_handle());
417-
let mut main_task = ~Task::new_root_homed(&mut main_sched.stack_pool,
418-
None,
419-
home,
420-
::util::replace(&mut main,
421-
None).
422-
unwrap());
423-
main_task.name = Some(SendStrStatic("<main>"));
424-
main_task.death.on_exit = ::util::replace(&mut on_exit, None);
425-
rtdebug!("bootstrapping main_task");
426-
427-
main_sched.bootstrap(main_task);
428-
}
429-
430-
rtdebug!("waiting for threads");
431-
432-
// Wait for schedulers
433-
for thread in threads.move_iter() {
434-
thread.join();
435-
}
436-
437-
// Return the exit code
438-
unsafe {
439-
(*exit_code.get()).load(SeqCst)
440-
}
441-
}
442-
443-
pub fn in_sched_context() -> bool {
444-
unsafe {
445-
let task_ptr: Option<*mut Task> = Local::try_unsafe_borrow();
446-
match task_ptr {
447-
Some(task) => {
448-
match (*task).task_type {
449-
SchedTask => true,
450-
_ => false
451-
}
452-
}
453-
None => false
454-
}
455-
}
456-
}
457-
458-
pub fn in_green_task_context() -> bool {
459-
unsafe {
460-
let task: Option<*mut Task> = Local::try_unsafe_borrow();
461-
match task {
462-
Some(task) => {
463-
match (*task).task_type {
464-
GreenTask(_) => true,
465-
_ => false
466-
}
467-
}
468-
None => false
469-
}
470-
}
471-
}
472-
473-
pub fn new_event_loop() -> ~rtio::EventLoop {
474-
match crate_map::get_crate_map() {
475-
None => {}
476-
Some(map) => {
477-
match map.event_loop_factory {
478-
None => {}
479-
Some(factory) => return factory()
480-
}
481-
}
482-
}
483-
484-
// If the crate map didn't specify a factory to create an event loop, then
485-
// instead just use a basic event loop missing all I/O services to at least
486-
// get the scheduler running.
487-
return basic::event_loop();
488-
}

0 commit comments

Comments
 (0)