Skip to content

std::rt: Add start_on_main_thread function #7864

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 61 additions & 10 deletions src/libstd/rt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ use clone::Clone;
use container::Container;
use iter::Times;
use iterator::IteratorUtil;
use option::Some;
use option::{Some, None};
use ptr::RawPtr;
use rt::sched::{Scheduler, Shutdown};
use rt::sleeper_list::SleeperList;
use rt::task::Task;
use rt::task::{Task, Sched};
use rt::thread::Thread;
use rt::work_queue::WorkQueue;
use rt::uv::uvio::UvEventLoop;
Expand Down Expand Up @@ -187,6 +187,19 @@ pub fn start(argc: int, argv: **u8, crate_map: *u8, main: ~fn()) -> int {
return exit_code;
}

/// Like `start` but creates an additional scheduler on the current thread,
/// which in most cases will be the 'main' thread, and pins the main task to it.
///
/// This is appropriate for running code that must execute on the main thread,
/// such as the platform event loop and GUI.
pub fn start_on_main_thread(argc: int, argv: **u8, crate_map: *u8, main: ~fn()) -> int {
init(argc, argv, crate_map);
let exit_code = run_on_main_thread(main);
cleanup();

return exit_code;
}

/// One-time runtime initialization.
///
/// Initializes global state, including frobbing
Expand Down Expand Up @@ -217,10 +230,17 @@ pub fn cleanup() {
/// using a task scheduler with the same number of threads as cores.
/// Returns a process exit code.
pub fn run(main: ~fn()) -> int {
run_(main, false)
}

pub fn run_on_main_thread(main: ~fn()) -> int {
run_(main, true)
}

fn run_(main: ~fn(), use_main_sched: bool) -> int {
static DEFAULT_ERROR_CODE: int = 101;

let nthreads = util::default_sched_threads();
let nscheds = util::default_sched_threads();

// The shared list of sleeping schedulers. Schedulers wake each other
// occassionally to do new work.
Expand All @@ -234,7 +254,7 @@ pub fn run(main: ~fn()) -> int {
// sent the Shutdown message to terminate the schedulers.
let mut handles = ~[];

for nthreads.times {
for nscheds.times {
// Every scheduler is driven by an I/O event loop.
let loop_ = ~UvEventLoop::new();
let mut sched = ~Scheduler::new(loop_, work_queue.clone(), sleepers.clone());
Expand All @@ -244,6 +264,21 @@ pub fn run(main: ~fn()) -> int {
handles.push(handle);
}

// If we need a main-thread task then create a main thread scheduler
// that will reject any task that isn't pinned to it
let mut main_sched = if use_main_sched {
let main_loop = ~UvEventLoop::new();
let mut main_sched = ~Scheduler::new_special(main_loop,
work_queue.clone(),
sleepers.clone(),
false);
let main_handle = main_sched.make_handle();
handles.push(main_handle);
Some(main_sched)
} else {
None
};

// Create a shared cell for transmitting the process exit
// code from the main task to this function.
let exit_code = UnsafeAtomicRcBox::new(AtomicInt::new(0));
Expand Down Expand Up @@ -273,12 +308,22 @@ pub fn run(main: ~fn()) -> int {
}
};

// Create and enqueue the main task.
let main_cell = Cell::new(main);
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool,
main_cell.take());
main_task.death.on_exit = Some(on_exit);
scheds[0].enqueue_task(main_task);
// Build the main task and queue it up
match main_sched {
None => {
// The default case where we don't need a scheduler on the main thread.
// Just put an unpinned task onto one of the default schedulers.
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool, main);
main_task.death.on_exit = Some(on_exit);
scheds[0].enqueue_task(main_task);
}
Some(ref mut main_sched) => {
let home = Sched(main_sched.make_handle());
let mut main_task = ~Task::new_root_homed(&mut scheds[0].stack_pool, home, main);
main_task.death.on_exit = Some(on_exit);
main_sched.enqueue_task(main_task);
}
};

// Run each scheduler in a thread.
let mut threads = ~[];
Expand All @@ -293,6 +338,12 @@ pub fn run(main: ~fn()) -> int {
threads.push(thread);
}

// Run the main-thread scheduler
match main_sched {
Some(sched) => { let _ = sched.run(); },
None => ()
}

// Wait for schedulers
{ let _threads = threads; }

Expand Down
21 changes: 21 additions & 0 deletions src/test/run-pass/rt-start-main-thread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// xfail-fast

#[start]
fn start(argc: int, argv: **u8, crate_map: *u8) -> int {
do std::rt::start_on_main_thread(argc, argv, crate_map) {
info!("running on main thread");
do spawn {
info!("running on another thread");
}
}
}