Skip to content

Commit

Permalink
auto merge of #8725 : bblum/rust/docs, r=graydon
Browse files Browse the repository at this point in the history
This documents how to use trait bounds in a (hopefully) user-friendly way, in the containers tutorial, and also documents the task watching implementation for runtime developers in kill.rs.

r anybody
  • Loading branch information
bors committed Aug 24, 2013
2 parents 424e8f0 + d468b7c commit db5615d
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 2 deletions.
32 changes: 31 additions & 1 deletion doc/tutorial.md
Expand Up @@ -1864,7 +1864,7 @@ so you could not apply `head` to a type
that does not implement `Clone`.

While most traits can be defined and implemented by user code,
two traits are automatically derived and implemented
three traits are automatically derived and implemented
for all applicable types by the compiler,
and may not be overridden:

Expand All @@ -1877,6 +1877,12 @@ These are types that do not contain anything intrinsically mutable.
Intrinsically mutable values include `@mut`
and `Cell` in the standard library.

* `'static` - Non-borrowed types.
These are types that do not contain any data whose lifetime is bound to
a particular stack frame. These are types that do not contain any
borrowed pointers, or types where the only contained borrowed pointers
have the `'static` lifetime.

> ***Note:*** These two traits were referred to as 'kinds' in earlier
> iterations of the language, and often still are.
Expand Down Expand Up @@ -2135,6 +2141,30 @@ select the method to call at runtime.

This usage of traits is similar to Java interfaces.

By default, each of the three storage classes for traits enforce a
particular set of built-in kinds that their contents must fulfill in
order to be packaged up in a trait object of that storage class.

* The contents of owned traits (`~Trait`) must fulfill the `Send` bound.
* The contents of managed traits (`@Trait`) must fulfill the `'static` bound.
* The contents of borrowed traits (`&Trait`) are not constrained by any bound.

Consequently, the trait objects themselves automatically fulfill their
respective kind bounds. However, this default behavior can be overridden by
specifying a list of bounds on the trait type, for example, by writing `~Trait:`
(which indicates that the contents of the owned trait need not fulfill any
bounds), or by writing `~Trait:Send+Freeze`, which indicates that in addition
to fulfilling `Send`, contents must also fulfill `Freeze`, and as a consequence,
the trait itself fulfills `Freeze`.

* `~Trait:Send` is equivalent to `~Trait`.
* `@Trait:'static` is equivalent to `@Trait`.
* `&Trait:` is equivalent to `&Trait`.

Builtin kind bounds can also be specified on closure types in the same way (for
example, by writing `fn:Freeze()`), and the default behaviours are the same as
for traits of the same storage class.

## Trait inheritance

We can write a trait declaration that _inherits_ from other traits, called _supertraits_.
Expand Down
86 changes: 85 additions & 1 deletion src/libstd/rt/kill.rs
Expand Up @@ -20,6 +20,7 @@ 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
Expand Down Expand Up @@ -60,9 +61,92 @@ 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.
The basic model for exit code propagation, which is used with the "watched"
spawn mode (on by default for linked spawns, off for supervised and unlinked
spawns), is that a parent will wait for all its watched children to exit
before reporting whether it succeeded or failed. A watching parent will only
report success if it succeeded and all its children also reported success;
otherwise, it will report failure. This is most useful for writing test cases:
~~~
#[test]
fn test_something_in_another_task {
do spawn {
assert!(collatz_conjecture_is_false());
}
}
~~~
Here, as the child task will certainly outlive the parent task, we might miss
the failure of the child when deciding whether or not the test case passed.
The watched spawn mode avoids this problem.
In order to propagate exit codes from children to their parents, any
'watching' parent must wait for all of its children to exit before it can
report its final exit status. We achieve this by using an UnsafeArc, using the
reference counting to track how many children are still alive, and using the
unwrap() operation in the parent's exit path to wait for all children to exit.
The UnsafeArc referred to here is actually the KillHandle itself.
This also works transitively, as if a "middle" watched child task is itself
watching a grandchild task, the "middle" task will do unwrap() on its own
KillHandle (thereby waiting for the grandchild to exit) before dropping its
reference to its watching parent (which will alert the parent).
While UnsafeArc::unwrap() accomplishes the synchronization, there remains the
matter of reporting the exit codes themselves. This is easiest when an exiting
watched task has no watched children of its own:
- If the task with no watched children exits successfully, it need do nothing.
- If the task with no watched children has failed, it sets a flag in the
parent's KillHandle ("any_child_failed") to false. It then stays false forever.
However, if a "middle" watched task with watched children of its own exits
before its child exits, we need to ensure that the grandparent task may still
see a failure from the grandchild task. While we could achieve this by having
each intermediate task block on its handle, this keeps around the other resources
the task was using. To be more efficient, this is accomplished via "tombstones".
A tombstone is a closure, ~fn() -> bool, which will perform any waiting necessary
to collect the exit code of descendant tasks. In its environment is captured
the KillHandle of whichever task created the tombstone, and perhaps also any
tombstones that that task itself had, and finally also another tombstone,
effectively creating a lazy-list of heap closures.
When a child wishes to exit early and leave tombstones behind for its parent,
it must use a LittleLock (pthread mutex) to synchronize with any possible
sibling tasks which are trying to do the same thing with the same parent.
However, on the other side, when the parent is ready to pull on the tombstones,
it need not use this lock, because the unwrap() serves as a barrier that ensures
no children will remain with references to the handle.
The main logic for creating and assigning tombstones can be found in the
function reparent_children_to() in the impl for KillHandle.
IIA. Issues with exit code propagation.
There are two known issues with the current scheme for exit code propagation.
- As documented in issue #8136, the structure mandates the possibility for stack
overflow when collecting tombstones that are very deeply nested. This cannot
be avoided with the closure representation, as tombstones end up structured in
a sort of tree. However, notably, the tombstones do not actually need to be
collected in any particular order, and so a doubly-linked list may be used.
However we do not do this yet because DList is in libextra.
- A discussion with Graydon made me realize that if we decoupled the exit code
propagation from the parents-waiting action, this could result in a simpler
implementation as the exit codes themselves would not have to be propagated,
and could instead be propagated implicitly through the taskgroup mechanism
that we already have. The tombstoning scheme would still be required. I have
not implemented this because currently we can't receive a linked failure kill
signal during the task cleanup activity, as that is currently "unkillable",
and occurs outside the task's unwinder's "try" block, so would require some
restructuring.
*/

Expand Down

0 comments on commit db5615d

Please sign in to comment.