Skip to content
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

Task killing, linked failure, and exit code propagation in the new runtime. #7858

Closed
wants to merge 29 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
96c1082
Add Either::expect_{left,right}
bblum Jul 2, 2013
5a9b33a
Add Option::take_map{,_default}()
bblum Jul 2, 2013
28c9ba9
Remove redundant Atomic{Ui,I}nt types from unstable::sync
bblum Jul 2, 2013
55adc44
Add AtomicOption::fill() and AtomicOption::is_empty()
bblum Jul 2, 2013
10a400f
Reimplement ARC::unwrap() and friends.
bblum Jul 2, 2013
2a99163
Add UnsafeAtomicRcBox::try_unwrap()
bblum Jul 2, 2013
52ca256
Add KillHandle and implement exit code propagation to replace join_latch
bblum Jul 3, 2013
6882508
Add kill::Death for task death services and use it in Task.
bblum Jul 2, 2013
afc199b
Remove join_latch
bblum Jul 2, 2013
2a99320
Add tests for KillHandle
bblum Jul 3, 2013
629f6e8
Implement KillHandle::kill() and friends (unkillable, atomically). Cl…
bblum Jul 8, 2013
e80efe3
Do a task-killed check at the start of task 'timeslices'.
bblum Jul 8, 2013
0101f35
Add BlockedTask (wake, try_block, etc) in kill.rs.
bblum Jul 11, 2013
9ad1997
Change the HOF context switchers to pass a BlockedTask instead of a ~…
bblum Jul 11, 2013
a093b54
Add test::with_test_task() convenience function.
bblum Jul 11, 2013
e283c4d
Add tests for task killing and blocking.
bblum Jul 11, 2013
2a7273c
Stash a spare kill flag inside tasks, to save two atomic xadds in the…
bblum Jul 12, 2013
e2a4241
Add option::take(), the building block of the option::take_* family.
bblum Jul 13, 2013
9bbec65
Replace *rust_task ptrs in taskgroup code with TaskHandle, for transi…
bblum Jul 13, 2013
87bbcb5
(cleanup) Modernize taskgroup code for the new borrow-checker.
bblum Jul 15, 2013
6d91846
(cleanup) Don't check taskgroup generation monotonicity unless cfg(te…
bblum Jul 15, 2013
728edb5
(cleanup) impl TaskSet
bblum Jul 15, 2013
0e1be5f
Fix linked failure tests to block forever instead of looping around y…
bblum Jul 16, 2013
f3c79c4
Enable taskgroup code for newsched spawns.
bblum Jul 15, 2013
2183145
Rename TCB to Taskgroup
bblum Jul 16, 2013
7ad7911
Add watched and indestructible spawn modes.
bblum Jul 16, 2013
4bcda71
Fix warnings in src/test/bench tests. Nobody will ever care.
bblum Jul 17, 2013
621bc79
Fix warnings in stdtest and extratest. Maybe somebody will care.
bblum Jul 17, 2013
980646a
Use Option .take() or .take_unwrap() instead of util::replace where p…
Jul 17, 2013
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
97 changes: 95 additions & 2 deletions src/libstd/rt/kill.rs
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@

use cast;
use cell::Cell;
use either::{Either, Left, Right};
use option::{Option, Some, None};
use prelude::*;
use rt::task::Task;
@@ -26,6 +27,16 @@ static KILL_RUNNING: uint = 0;
static KILL_KILLED: uint = 1;
static KILL_UNKILLABLE: uint = 2;

struct KillFlag(AtomicUint);
type KillFlagHandle = UnsafeAtomicRcBox<KillFlag>;

/// A handle to a blocked task. Usually this means having the ~Task pointer by
/// ownership, but if the task is killable, a killer can steal it at any time.
pub enum BlockedTask {
Unkillable(~Task),
Killable(KillFlagHandle),
}

// FIXME(#7544)(bblum): think about the cache efficiency of this
struct KillHandleInner {
// Is the task running, blocked, or killed? Possible values:
@@ -35,7 +46,7 @@ struct KillHandleInner {
// This flag is refcounted because it may also be referenced by a blocking
// concurrency primitive, used to wake the task normally, whose reference
// may outlive the handle's if the task is killed.
killed: UnsafeAtomicRcBox<AtomicUint>,
killed: KillFlagHandle,
// Has the task deferred kill signals? This flag guards the above one.
// Possible values:
// * KILL_RUNNING - Not unkillable, no kill pending.
@@ -76,11 +87,93 @@ pub struct Death {
wont_sleep: int,
}

impl Drop for KillFlag {
// Letting a KillFlag with a task inside get dropped would leak the task.
// We could free it here, but the task should get awoken by hand somehow.
fn drop(&self) {
match self.load(Acquire) {
KILL_RUNNING | KILL_KILLED => { },
_ => rtabort!("can't drop kill flag with a blocked task inside!"),
}
}
}

impl BlockedTask {
/// Returns Some if the task was successfully woken; None if already killed.
pub fn wake(self) -> Option<~Task> {
let mut this = self;
match this {
Unkillable(task) => Some(task),
Killable(ref mut flag_arc) => {
let flag = unsafe { &mut **flag_arc.get() };
match flag.swap(KILL_RUNNING, SeqCst) {
KILL_RUNNING => rtabort!("tried to wake an already-running task"),
KILL_KILLED => None, // a killer stole it already
task_ptr => Some(unsafe { cast::transmute(task_ptr) }),
}
}
}
}

/// Create a blocked task, unless the task was already killed.
pub fn try_block(task: ~Task) -> Either<~Task, BlockedTask> {
if task.death.unkillable > 0 { // FIXME(#7544): || self.indestructible
Right(Unkillable(task))
} else {
rtassert!(task.death.kill_handle.is_some());
unsafe {
// FIXME(#7544) optimz
let flag_arc = (*task.death.kill_handle.get_ref().get()).killed.clone();
let flag = &mut **flag_arc.get();
let task_ptr = cast::transmute(task);
// Expect flag to contain RUNNING. If KILLED, it should stay KILLED.
match flag.compare_and_swap(KILL_RUNNING, task_ptr, SeqCst) {
KILL_RUNNING => Right(Killable(flag_arc)),
KILL_KILLED => Left(cast::transmute(task_ptr)),
x => rtabort!("can't block task! kill flag = %?", x),
}
}
}
}

/// Convert to an unsafe uint value. Useful for storing in a pipe's state flag.
#[inline]
pub unsafe fn cast_to_uint(self) -> uint {
// Use the low bit to distinguish the enum variants, to save a second
// allocation in the indestructible case.
match self {
Unkillable(task) => {
let blocked_task_ptr: uint = cast::transmute(task);
rtassert!(blocked_task_ptr & 0x1 == 0);
blocked_task_ptr
},
Killable(flag_arc) => {
let blocked_task_ptr: uint = cast::transmute(~flag_arc);
rtassert!(blocked_task_ptr & 0x1 == 0);
blocked_task_ptr | 0x1
}
}
}

/// Convert from an unsafe uint value. Useful for retrieving a pipe's state flag.
#[inline]
pub unsafe fn cast_from_uint(blocked_task_ptr: uint) -> BlockedTask {
if blocked_task_ptr & 0x1 == 0 {
Unkillable(cast::transmute(blocked_task_ptr))
} else {
let ptr: ~KillFlagHandle = cast::transmute(blocked_task_ptr & !0x1);
match ptr {
~flag_arc => Killable(flag_arc)
}
}
}
}

impl KillHandle {
pub fn new() -> KillHandle {
KillHandle(UnsafeAtomicRcBox::new(KillHandleInner {
// Linked failure fields
killed: UnsafeAtomicRcBox::new(AtomicUint::new(KILL_RUNNING)),
killed: UnsafeAtomicRcBox::new(KillFlag(AtomicUint::new(KILL_RUNNING))),
unkillable: AtomicUint::new(KILL_RUNNING),
// Exit code propagation fields
any_child_failed: false,