diff options
| author | Ben Blum <bblum@andrew.cmu.edu> | 2013-07-31 19:14:59 -0400 |
|---|---|---|
| committer | Ben Blum <bblum@andrew.cmu.edu> | 2013-08-01 16:52:37 -0400 |
| commit | 036a6d2f0080abd10521f7999af7100e5bcaaffe (patch) | |
| tree | e78b403dc5d7735ffe7360b93b719dd83b372050 /src/libstd/rt | |
| parent | 880246618b6c28adc76e5e68247aa5a9302320f8 (diff) | |
| download | rust-036a6d2f0080abd10521f7999af7100e5bcaaffe.tar.gz rust-036a6d2f0080abd10521f7999af7100e5bcaaffe.zip | |
Document task killing design and relaxed barrier rationale.
Diffstat (limited to 'src/libstd/rt')
| -rw-r--r-- | src/libstd/rt/kill.rs | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/src/libstd/rt/kill.rs b/src/libstd/rt/kill.rs index 729c682dbd1..208af522d80 100644 --- a/src/libstd/rt/kill.rs +++ b/src/libstd/rt/kill.rs @@ -8,7 +8,63 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Task death: asynchronous killing, linked failure, exit code propagation. +/*! + +Task death: asynchronous killing, linked failure, exit code propagation. + +This file implements two orthogonal building-blocks for communicating failure +between tasks. One is 'linked failure' or 'task killing', that is, a failing +task causing other tasks to fail promptly (even those that are blocked on +pipes or I/O). The other is 'exit code propagation', which affects the result +observed by the parent of a task::try task that itself spawns child tasks +(such as any #[test] function). In both cases the data structures live in +KillHandle. + +I. Task killing. + +The model for killing involves two atomic flags, the "kill flag" and the +"unkillable flag". Operations on the kill flag include: + +- In the taskgroup code (task/spawn.rs), tasks store a clone of their + KillHandle in their shared taskgroup. Another task in the group that fails + will use that handle to call kill(). +- When a task blocks, it turns its ~Task into a BlockedTask by storing a + the transmuted ~Task pointer inside the KillHandle's kill flag. A task + trying to block and a task trying to kill it can simultaneously access the + kill flag, after which the task will get scheduled and fail (no matter who + wins the race). Likewise, a task trying to wake a blocked task normally and + a task trying to kill it can simultaneously access the flag; only one will + get the task to reschedule it. + +Operations on the unkillable flag include: + +- When a task becomes unkillable, it swaps on the flag to forbid any killer + from waking it up while it's blocked inside the unkillable section. If a + kill was already pending, the task fails instead of becoming unkillable. +- When a task is done being unkillable, it restores the flag to the normal + running state. If a kill was received-but-blocked during the unkillable + section, the task fails at this later point. +- When a task tries to kill another task, before swapping on the kill flag, it + first swaps on the unkillable flag, to see if it's "allowed" to wake up the + task. If it isn't, the killed task will receive the signal when it becomes + killable again. (Of course, a task trying to wake the task normally (e.g. + sending on a channel) does not access the unkillable flag at all.) + +Why do we not need acquire/release barriers on any of the kill flag swaps? +This is because barriers establish orderings between accesses on different +memory locations, but each kill-related operation is only a swap on a single +location, so atomicity is all that matters. The exception is kill(), which +does a swap on both flags in sequence. kill() needs no barriers because it +does not matter if its two accesses are seen reordered on another CPU: if a +killer does perform both writes, it means it saw a KILL_RUNNING in the +unkillable flag, which means an unkillable task will see KILL_KILLED and fail +immediately (rendering the subsequent write to the kill flag unnecessary). + +II. Exit code propagation. + +FIXME(#7544): Decide on the ultimate model for this and document it. + +*/ use cast; use cell::Cell; |
