Skip to content

Allow tasks to be named, and clean up task::atomically. #8158

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 2 commits 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
2 changes: 1 addition & 1 deletion src/libstd/rt/kill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub struct Death {
on_exit: Option<~fn(bool)>,
// nesting level counter for task::unkillable calls (0 == killable).
unkillable: int,
// nesting level counter for task::atomically calls (0 == can yield).
// nesting level counter for unstable::atomically calls (0 == can yield).
wont_sleep: int,
// A "spare" handle to the kill flag inside the kill handle. Used during
// blocking/waking as an optimization to avoid two xadds on the refcount.
Expand Down
2 changes: 2 additions & 0 deletions src/libstd/rt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,12 +316,14 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
// 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);
main_task.name = Some(~"main");
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_task.name = Some(~"main");
main_sched.enqueue_task(main_task);
}
};
Expand Down
10 changes: 7 additions & 3 deletions src/libstd/rt/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ pub struct Task {
taskgroup: Option<Taskgroup>,
death: Death,
destroyed: bool,
coroutine: Option<~Coroutine>
coroutine: Option<~Coroutine>,
// FIXME(#6874/#7599) use StringRef to save on allocations
name: Option<~str>,
}

pub struct Coroutine {
Expand Down Expand Up @@ -90,7 +92,8 @@ impl Task {
taskgroup: None,
death: Death::new(),
destroyed: false,
coroutine: Some(~Coroutine::new(stack_pool, start))
coroutine: Some(~Coroutine::new(stack_pool, start)),
name: None,
}
}

Expand All @@ -109,7 +112,8 @@ impl Task {
// FIXME(#7544) make watching optional
death: self.death.new_child(),
destroyed: false,
coroutine: Some(~Coroutine::new(stack_pool, start))
coroutine: Some(~Coroutine::new(stack_pool, start)),
name: None,
}
}

Expand Down
21 changes: 14 additions & 7 deletions src/libstd/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,13 @@ impl FailWithCause for &'static str {

// FIXME #4427: Temporary until rt::rt_fail_ goes away
pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
use cell::Cell;
use either::Left;
use option::{Some, None};
use rt::{context, OldTaskContext, TaskContext};
use rt::task::Task;
use rt::local::Local;
use rt::logging::Logger;
use str::Str;

let context = context();
match context {
Expand All @@ -159,20 +160,26 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
let msg = str::raw::from_c_str(msg);
let file = str::raw::from_c_str(file);

let outmsg = fmt!("task failed at '%s', %s:%i",
msg, file, line as int);

// XXX: Logging doesn't work correctly in non-task context because it
// invokes the local heap
if context == TaskContext {
// XXX: Logging doesn't work here - the check to call the log
// function never passes - so calling the log function directly.
let outmsg = Cell::new(outmsg);
do Local::borrow::<Task, ()> |task| {
task.logger.log(Left(outmsg.take()));
let msg = match task.name {
Some(ref name) =>
fmt!("task '%s' failed at '%s', %s:%i",
name.as_slice(), msg, file, line as int),
None =>
fmt!("task <unnamed> failed at '%s', %s:%i",
msg, file, line as int)
};

task.logger.log(Left(msg));
}
} else {
rterrln!("%s", outmsg);
rterrln!("failed in non-task context at '%s', %s:%i",
msg, file, line as int);
}

gc::cleanup_stack_for_failure();
Expand Down
112 changes: 57 additions & 55 deletions src/libstd/task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ pub struct SchedOpts {
*
* * notify_chan - Enable lifecycle notifications on the given channel
*
* * name - A name for the task-to-be, for identification in failure messages.
*
* * sched - Specify the configuration of a new scheduler to create the task
* in
*
Expand All @@ -139,6 +141,7 @@ pub struct TaskOpts {
watched: bool,
indestructible: bool,
notify_chan: Option<Chan<TaskResult>>,
name: Option<~str>,
sched: SchedOpts
}

Expand Down Expand Up @@ -185,23 +188,23 @@ impl TaskBuilder {
self.consumed = true;
let gen_body = self.gen_body.take();
let notify_chan = self.opts.notify_chan.take();
let name = self.opts.name.take();
TaskBuilder {
opts: TaskOpts {
linked: self.opts.linked,
supervised: self.opts.supervised,
watched: self.opts.watched,
indestructible: self.opts.indestructible,
notify_chan: notify_chan,
name: name,
sched: self.opts.sched
},
gen_body: gen_body,
can_not_copy: None,
consumed: false
}
}
}

impl TaskBuilder {
/// Decouple the child task's failure from the parent's. If either fails,
/// the other will not be killed.
pub fn unlinked(&mut self) {
Expand Down Expand Up @@ -281,6 +284,12 @@ impl TaskBuilder {
self.opts.notify_chan = Some(notify_pipe_ch);
}

/// Name the task-to-be. Currently the name is used for identification
/// only in failure messages.
pub fn name(&mut self, name: ~str) {
self.opts.name = Some(name);
}

/// Configure a custom scheduler mode for the task.
pub fn sched_mode(&mut self, mode: SchedMode) {
self.opts.sched.mode = mode;
Expand Down Expand Up @@ -333,13 +342,15 @@ impl TaskBuilder {
pub fn spawn(&mut self, f: ~fn()) {
let gen_body = self.gen_body.take();
let notify_chan = self.opts.notify_chan.take();
let name = self.opts.name.take();
let x = self.consume();
let opts = TaskOpts {
linked: x.opts.linked,
supervised: x.opts.supervised,
watched: x.opts.watched,
indestructible: x.opts.indestructible,
notify_chan: notify_chan,
name: name,
sched: x.opts.sched
};
let f = match gen_body {
Expand Down Expand Up @@ -408,6 +419,7 @@ pub fn default_task_opts() -> TaskOpts {
watched: true,
indestructible: false,
notify_chan: None,
name: None,
sched: SchedOpts {
mode: DefaultScheduler,
}
Expand Down Expand Up @@ -507,6 +519,21 @@ pub fn try<T:Send>(f: ~fn() -> T) -> Result<T,()> {

/* Lifecycle functions */

/// Read the name of the current task.
pub fn with_task_name<U>(blk: &fn(Option<&str>) -> U) -> U {
use rt::task::Task;

match context() {
TaskContext => do Local::borrow::<Task, U> |task| {
match task.name {
Some(ref name) => blk(Some(name.as_slice())),
None => blk(None)
}
},
_ => fail!("no task name exists in %?", context()),
}
}

pub fn yield() {
//! Yield control to the task scheduler

Expand Down Expand Up @@ -628,44 +655,6 @@ pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
}
}

/**
* A stronger version of unkillable that also inhibits scheduling operations.
* For use with exclusive Arcs, which use pthread mutexes directly.
*/
pub unsafe fn atomically<U>(f: &fn() -> U) -> U {
use rt::task::Task;

match context() {
OldTaskContext => {
let t = rt::rust_get_task();
do (|| {
rt::rust_task_inhibit_kill(t);
rt::rust_task_inhibit_yield(t);
f()
}).finally {
rt::rust_task_allow_yield(t);
rt::rust_task_allow_kill(t);
}
}
TaskContext => {
let t = Local::unsafe_borrow::<Task>();
do (|| {
// It's important to inhibit kill after inhibiting yield, because
// inhibit-kill might fail if we were already killed, and the
// inhibit-yield must happen to match the finally's allow-yield.
(*t).death.inhibit_yield();
(*t).death.inhibit_kill((*t).unwinder.unwinding);
f()
}).finally {
(*t).death.allow_kill((*t).unwinder.unwinding);
(*t).death.allow_yield();
}
}
// FIXME(#3095): As in unkillable().
_ => f()
}
}

#[test] #[should_fail] #[ignore(cfg(windows))]
fn test_cant_dup_task_builder() {
let mut builder = task();
Expand Down Expand Up @@ -805,6 +794,34 @@ fn test_spawn_linked_sup_propagate_sibling() {
fail!();
}

#[test]
fn test_unnamed_task() {
use rt::test::run_in_newsched_task;

do run_in_newsched_task {
do spawn {
do with_task_name |name| {
assert!(name.is_none());
}
}
}
}

#[test]
fn test_named_task() {
use rt::test::run_in_newsched_task;

do run_in_newsched_task {
let mut t = task();
t.name(~"ada lovelace");
do t.spawn {
do with_task_name |name| {
assert!(name.get() == "ada lovelace");
}
}
}
}

#[test]
fn test_run_basic() {
let (po, ch) = stream::<()>();
Expand Down Expand Up @@ -1122,21 +1139,6 @@ fn test_unkillable_nested() {
po.recv();
}

#[test] #[should_fail] #[ignore(cfg(windows))]
fn test_atomically() {
unsafe { do atomically { yield(); } }
}

#[test]
fn test_atomically2() {
unsafe { do atomically { } } yield(); // shouldn't fail
}

#[test] #[should_fail] #[ignore(cfg(windows))]
fn test_atomically_nested() {
unsafe { do atomically { do atomically { } yield(); } }
}

#[test]
fn test_child_doesnt_ref_parent() {
// If the child refcounts the parent task, this will stack overflow when
Expand Down
2 changes: 2 additions & 0 deletions src/libstd/task/spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,8 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
task.death.on_exit = Some(on_exit);
}

task.name = opts.name.take();

rtdebug!("spawn about to take scheduler");

let sched = Local::take::<Scheduler>();
Expand Down
8 changes: 4 additions & 4 deletions src/libstd/unstable/dynamic_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ mod dl {
use path;
use ptr;
use str;
use task;
use unstable::sync::atomically;
use result::*;

pub unsafe fn open_external(filename: &path::Path) -> *libc::c_void {
Expand All @@ -120,7 +120,7 @@ mod dl {

pub fn check_for_errors_in<T>(f: &fn()->T) -> Result<T, ~str> {
unsafe {
do task::atomically {
do atomically {
let _old_error = dlerror();

let result = f();
Expand Down Expand Up @@ -164,7 +164,7 @@ mod dl {
use libc;
use path;
use ptr;
use task;
use unstable::sync::atomically;
use result::*;

pub unsafe fn open_external(filename: &path::Path) -> *libc::c_void {
Expand All @@ -181,7 +181,7 @@ mod dl {

pub fn check_for_errors_in<T>(f: &fn()->T) -> Result<T, ~str> {
unsafe {
do task::atomically {
do atomically {
SetLastError(0);

let result = f();
Expand Down
4 changes: 2 additions & 2 deletions src/libstd/unstable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ fn test_run_in_bare_thread_exchange() {
pub fn change_dir_locked(p: &Path, action: &fn()) -> bool {
use os;
use os::change_dir;
use task;
use unstable::sync::atomically;
use unstable::finally::Finally;

unsafe {
// This is really sketchy. Using a pthread mutex so descheduling
// in the `action` callback can cause deadlock. Doing it in
// `task::atomically` to try to avoid that, but ... I don't know
// this is all bogus.
return do task::atomically {
return do atomically {
rust_take_change_dir_lock();

do (||{
Expand Down
Loading