Skip to content

New task API #1867

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
84 changes: 40 additions & 44 deletions doc/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -2375,10 +2375,10 @@ module `task`. Let's begin with the simplest one, `task::spawn()`:

~~~~
let some_value = 22;
let child_task = task::spawn {||
task::spawn {||
std::io::println("This executes in the child task.");
std::io::println(#fmt("%d", some_value));
};
}
~~~~

The argument to `task::spawn()` is a [unique
Expand Down Expand Up @@ -2456,70 +2456,66 @@ let result = comm::recv(port);
## Creating a task with a bi-directional communication path

A very common thing to do is to spawn a child task where the parent
and child both need to exchange messages with each other. The function
`task::spawn_connected()` supports this pattern. We'll look briefly at
how it is used.
and child both need to exchange messages with each
other. The function `task::spawn_listener()` supports this pattern. We'll look
briefly at how it is used.

To see how `spawn_connected()` works, we will create a child task
To see how `spawn_listener()` works, we will create a child task
which receives `uint` messages, converts them to a string, and sends
the string in response. The child terminates when `0` is received.
Here is the function which implements the child task:

~~~~
fn stringifier(from_par: comm::port<uint>,
to_par: comm::chan<str>) {
fn stringifier(from_parent: comm::port<uint>,
to_parent: comm::chan<str>) {
let value: uint;
do {
value = comm::recv(from_par);
comm::send(to_par, uint::to_str(value, 10u));
value = comm::recv(from_parent);
comm::send(to_parent, uint::to_str(value, 10u));
} while value != 0u;
}

~~~~

You can see that the function takes two parameters. The first is a
port used to receive messages from the parent, and the second is a
channel used to send messages to the parent. The body itself simply
loops, reading from the `from_par` port and then sending its response
to the `to_par` channel. The actual response itself is simply the
strified version of the received value, `uint::to_str(value)`.

Here is the code for the parent task:

~~~~
# fn stringifier(from_par: comm::port<uint>,
# to_par: comm::chan<str>) {
# comm::send(to_par, "22");
# comm::send(to_par, "23");
# comm::send(to_par, "0");
# fn stringifier(from_parent: comm::port<uint>,
# to_parent: comm::chan<str>) {
# comm::send(to_parent, "22");
# comm::send(to_parent, "23");
# comm::send(to_parent, "0");
# }
fn main() {
let t = task::spawn_connected(stringifier);
comm::send(t.to_child, 22u);
assert comm::recv(t.from_child) == "22";
comm::send(t.to_child, 23u);
assert comm::recv(t.from_child) == "23";
comm::send(t.to_child, 0u);
assert comm::recv(t.from_child) == "0";
}
~~~~

The call to `spawn_connected()` on the first line will instantiate the
various ports and channels and startup the child task. The returned
value, `t`, is a record of type `task::connected_task<uint,str>`. In
addition to the task id of the child, this record defines two fields,
`from_child` and `to_child`, which contain the port and channel
respectively for communicating with the child. Those fields are used
here to send and receive three messages from the child task.

## Joining a task

The function `spawn_joinable()` is used to spawn a task that can later
be joined. This is implemented by having the child task send a message
when it has completed (either successfully or by failing). Therefore,
`spawn_joinable()` returns a structure containing both the task ID and
the port where this message will be sent---this structure type is
called `task::joinable_task`. The structure can be passed to
`task::join()`, which simply blocks on the port, waiting to receive
the message from the child task.
let from_child = comm::port();
let to_parent = comm::chan(from_child);
let to_child = task::spawn_listener {|from_parent|
stringifier(from_parent, to_parent);
};
comm::send(to_child, 22u);
assert comm::recv(from_child) == "22";
comm::send(to_child, 23u);
assert comm::recv(from_child) == "23";
comm::send(to_child, 0u);
assert comm::recv(from_child) == "0";
}
~~~~

The parent first sets up a port to receive data from and a channel
that the child can use to send data to that port. The call to
`spawn_listener()` will spawn the child task, providing it with a port
on which to receive data from its parent, and returning to the parent
the associated channel. Finally, the closure passed to
`spawn_listener()` that forms the body of the child task captures the
`to_parent` channel in its environment, so both parent and child
can send and receive data to and from the other.

## The supervisor relationship

Expand Down
2 changes: 0 additions & 2 deletions src/comp/driver/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,6 @@ fn monitor(f: fn~(diagnostic::emitter)) {

alt task::try {||

task::unsupervise();

// The 'diagnostics emitter'. Every error, warning, etc. should
// go through this function.
let demitter = fn@(cmsp: option<(codemap::codemap, codemap::span)>,
Expand Down
4 changes: 2 additions & 2 deletions src/compiletest/procsrv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ fn run(lib_path: str, prog: str, args: [str],
writeclose(pipe_in.out, input);
let p = comm::port();
let ch = comm::chan(p);
task::spawn_sched(1u) {||
task::spawn_sched(task::single_threaded) {||
let errput = readclose(pipe_err.in);
comm::send(ch, (2, errput));
};
task::spawn_sched(1u) {||
task::spawn_sched(task::single_threaded) {||
let output = readclose(pipe_out.in);
comm::send(ch, (1, output));
};
Expand Down
8 changes: 5 additions & 3 deletions src/libcore/comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ enum rust_port {}

#[abi = "cdecl"]
native mod rustrt {
fn get_task_id() -> task_id;
fn chan_id_send<T: send>(t: *sys::type_desc,
target_task: task::task, target_port: port_id,
target_task: task_id, target_port: port_id,
data: T) -> ctypes::uintptr_t;

fn new_port(unit_sz: ctypes::size_t) -> *rust_port;
Expand All @@ -58,6 +59,7 @@ native mod rusti {
fn call_with_retptr<T: send>(&&f: fn@(*uint)) -> T;
}

type task_id = int;
type port_id = int;

// It's critical that this only have one variant, so it has a record
Expand All @@ -75,7 +77,7 @@ type port_id = int;
over other channels."
)]
enum chan<T: send> {
chan_t(task::task, port_id)
chan_t(task_id, port_id)
}

resource port_ptr<T: send>(po: *rust_port) {
Expand Down Expand Up @@ -208,7 +210,7 @@ fn peek<T: send>(p: port<T>) -> bool {
port used to construct it."
)]
fn chan<T: send>(p: port<T>) -> chan<T> {
chan_t(task::get_task(), rustrt::get_port_id(***p))
chan_t(rustrt::get_task_id(), rustrt::get_port_id(***p))
}

#[test]
Expand Down
Loading