2012-08-14 18:25:42 -05:00
|
|
|
// NB: transitionary, de-mode-ing.
|
|
|
|
#[forbid(deprecated_mode)];
|
|
|
|
#[forbid(deprecated_pattern)];
|
|
|
|
|
2012-04-04 23:17:50 -05:00
|
|
|
#[doc(hidden)];
|
|
|
|
|
2012-04-12 00:14:45 -05:00
|
|
|
export chan_from_global_ptr, weaken_task;
|
2012-04-04 23:17:50 -05:00
|
|
|
|
|
|
|
import compare_and_swap = rustrt::rust_compare_and_swap_ptr;
|
2012-08-15 16:10:46 -05:00
|
|
|
import task::TaskBuilder;
|
2012-04-04 23:17:50 -05:00
|
|
|
|
2012-08-13 18:20:27 -05:00
|
|
|
#[allow(non_camel_case_types)] // runtime type
|
2012-04-06 19:03:00 -05:00
|
|
|
type rust_port_id = uint;
|
|
|
|
|
2012-07-03 18:11:00 -05:00
|
|
|
extern mod rustrt {
|
2012-04-04 23:17:50 -05:00
|
|
|
fn rust_compare_and_swap_ptr(address: *libc::uintptr_t,
|
|
|
|
oldval: libc::uintptr_t,
|
|
|
|
newval: libc::uintptr_t) -> bool;
|
2012-04-06 19:03:00 -05:00
|
|
|
fn rust_task_weaken(ch: rust_port_id);
|
|
|
|
fn rust_task_unweaken(ch: rust_port_id);
|
2012-04-04 23:17:50 -05:00
|
|
|
}
|
|
|
|
|
2012-08-13 18:20:27 -05:00
|
|
|
type GlobalPtr = *libc::uintptr_t;
|
2012-04-04 23:17:50 -05:00
|
|
|
|
2012-07-04 16:53:12 -05:00
|
|
|
/**
|
|
|
|
* Atomically gets a channel from a pointer to a pointer-sized memory location
|
|
|
|
* or, if no channel exists creates and installs a new channel and sets up a
|
|
|
|
* new task to receive from it.
|
|
|
|
*/
|
2012-04-04 23:17:50 -05:00
|
|
|
unsafe fn chan_from_global_ptr<T: send>(
|
2012-08-13 18:20:27 -05:00
|
|
|
global: GlobalPtr,
|
2012-08-15 16:10:46 -05:00
|
|
|
task_fn: fn() -> task::TaskBuilder,
|
|
|
|
+f: fn~(comm::Port<T>)
|
|
|
|
) -> comm::Chan<T> {
|
2012-04-04 23:17:50 -05:00
|
|
|
|
2012-08-13 18:20:27 -05:00
|
|
|
enum Msg {
|
|
|
|
Proceed,
|
|
|
|
Abort
|
2012-04-04 23:17:50 -05:00
|
|
|
}
|
|
|
|
|
2012-07-14 00:57:48 -05:00
|
|
|
log(debug,~"ENTERING chan_from_global_ptr, before is_prob_zero check");
|
2012-04-04 23:17:50 -05:00
|
|
|
let is_probably_zero = *global == 0u;
|
2012-07-14 00:57:48 -05:00
|
|
|
log(debug,~"after is_prob_zero check");
|
2012-04-04 23:17:50 -05:00
|
|
|
if is_probably_zero {
|
2012-07-14 00:57:48 -05:00
|
|
|
log(debug,~"is probably zero...");
|
2012-04-04 23:17:50 -05:00
|
|
|
// There's no global channel. We must make it
|
|
|
|
|
2012-08-08 15:38:26 -05:00
|
|
|
let (setup_po, setup_ch) = do task_fn().spawn_conversation
|
|
|
|
|setup_po, setup_ch| {
|
2012-08-27 16:22:25 -05:00
|
|
|
let po = comm::Port::<T>();
|
|
|
|
let ch = comm::Chan(po);
|
2012-04-04 23:17:50 -05:00
|
|
|
comm::send(setup_ch, ch);
|
|
|
|
|
|
|
|
// Wait to hear if we are the official instance of
|
|
|
|
// this global task
|
2012-08-13 18:20:27 -05:00
|
|
|
match comm::recv::<Msg>(setup_po) {
|
|
|
|
Proceed => f(po),
|
|
|
|
Abort => ()
|
2012-04-04 23:17:50 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-07-14 00:57:48 -05:00
|
|
|
log(debug,~"before setup recv..");
|
2012-04-04 23:17:50 -05:00
|
|
|
// This is the proposed global channel
|
|
|
|
let ch = comm::recv(setup_po);
|
|
|
|
// 0 is our sentinal value. It is not a valid channel
|
|
|
|
assert unsafe::reinterpret_cast(ch) != 0u;
|
|
|
|
|
|
|
|
// Install the channel
|
2012-07-14 00:57:48 -05:00
|
|
|
log(debug,~"BEFORE COMPARE AND SWAP");
|
2012-04-04 23:17:50 -05:00
|
|
|
let swapped = compare_and_swap(
|
|
|
|
global, 0u, unsafe::reinterpret_cast(ch));
|
2012-08-22 19:24:52 -05:00
|
|
|
log(debug,fmt!("AFTER .. swapped? %?", swapped));
|
2012-04-04 23:17:50 -05:00
|
|
|
|
|
|
|
if swapped {
|
|
|
|
// Success!
|
2012-08-13 18:20:27 -05:00
|
|
|
comm::send(setup_ch, Proceed);
|
2012-04-04 23:17:50 -05:00
|
|
|
ch
|
|
|
|
} else {
|
|
|
|
// Somebody else got in before we did
|
2012-08-13 18:20:27 -05:00
|
|
|
comm::send(setup_ch, Abort);
|
2012-04-04 23:17:50 -05:00
|
|
|
unsafe::reinterpret_cast(*global)
|
|
|
|
}
|
|
|
|
} else {
|
2012-07-14 00:57:48 -05:00
|
|
|
log(debug, ~"global != 0");
|
2012-04-04 23:17:50 -05:00
|
|
|
unsafe::reinterpret_cast(*global)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2012-06-24 22:18:18 -05:00
|
|
|
fn test_from_global_chan1() {
|
2012-04-04 23:17:50 -05:00
|
|
|
|
|
|
|
// This is unreadable, right?
|
|
|
|
|
|
|
|
// The global channel
|
|
|
|
let globchan = 0u;
|
|
|
|
let globchanp = ptr::addr_of(globchan);
|
|
|
|
|
|
|
|
// Create the global channel, attached to a new task
|
2012-06-24 22:18:18 -05:00
|
|
|
let ch = unsafe {
|
2012-07-23 18:53:22 -05:00
|
|
|
do chan_from_global_ptr(globchanp, task::task) |po| {
|
2012-06-24 22:18:18 -05:00
|
|
|
let ch = comm::recv(po);
|
|
|
|
comm::send(ch, true);
|
|
|
|
let ch = comm::recv(po);
|
|
|
|
comm::send(ch, true);
|
|
|
|
}
|
2012-04-04 23:17:50 -05:00
|
|
|
};
|
|
|
|
// Talk to it
|
2012-08-27 16:22:25 -05:00
|
|
|
let po = comm::Port();
|
|
|
|
comm::send(ch, comm::Chan(po));
|
2012-04-04 23:17:50 -05:00
|
|
|
assert comm::recv(po) == true;
|
|
|
|
|
|
|
|
// This one just reuses the previous channel
|
2012-06-24 22:18:18 -05:00
|
|
|
let ch = unsafe {
|
2012-07-23 18:53:22 -05:00
|
|
|
do chan_from_global_ptr(globchanp, task::task) |po| {
|
2012-06-24 22:18:18 -05:00
|
|
|
let ch = comm::recv(po);
|
|
|
|
comm::send(ch, false);
|
|
|
|
}
|
2012-04-04 23:17:50 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
// Talk to the original global task
|
2012-08-27 16:22:25 -05:00
|
|
|
let po = comm::Port();
|
|
|
|
comm::send(ch, comm::Chan(po));
|
2012-04-04 23:17:50 -05:00
|
|
|
assert comm::recv(po) == true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2012-06-24 22:18:18 -05:00
|
|
|
fn test_from_global_chan2() {
|
2012-04-04 23:17:50 -05:00
|
|
|
|
2012-07-04 14:04:28 -05:00
|
|
|
for iter::repeat(100u) {
|
2012-04-04 23:17:50 -05:00
|
|
|
// The global channel
|
|
|
|
let globchan = 0u;
|
|
|
|
let globchanp = ptr::addr_of(globchan);
|
|
|
|
|
2012-08-27 16:22:25 -05:00
|
|
|
let resultpo = comm::Port();
|
|
|
|
let resultch = comm::Chan(resultpo);
|
2012-04-04 23:17:50 -05:00
|
|
|
|
|
|
|
// Spawn a bunch of tasks that all want to compete to
|
|
|
|
// create the global channel
|
2012-06-30 18:19:07 -05:00
|
|
|
for uint::range(0u, 10u) |i| {
|
2012-07-04 14:04:28 -05:00
|
|
|
do task::spawn {
|
2012-06-24 22:18:18 -05:00
|
|
|
let ch = unsafe {
|
2012-06-26 15:55:56 -05:00
|
|
|
do chan_from_global_ptr(
|
2012-07-23 18:53:22 -05:00
|
|
|
globchanp, task::task) |po| {
|
2012-06-24 22:18:18 -05:00
|
|
|
|
2012-06-30 18:19:07 -05:00
|
|
|
for uint::range(0u, 10u) |_j| {
|
2012-06-24 22:18:18 -05:00
|
|
|
let ch = comm::recv(po);
|
|
|
|
comm::send(ch, {i});
|
|
|
|
}
|
2012-04-04 23:17:50 -05:00
|
|
|
}
|
|
|
|
};
|
2012-08-27 16:22:25 -05:00
|
|
|
let po = comm::Port();
|
|
|
|
comm::send(ch, comm::Chan(po));
|
|
|
|
// We are The winner if our version of the
|
2012-04-04 23:17:50 -05:00
|
|
|
// task was installed
|
|
|
|
let winner = comm::recv(po);
|
|
|
|
comm::send(resultch, winner == i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// There should be only one winner
|
|
|
|
let mut winners = 0u;
|
2012-06-30 18:19:07 -05:00
|
|
|
for uint::range(0u, 10u) |_i| {
|
2012-04-04 23:17:50 -05:00
|
|
|
let res = comm::recv(resultpo);
|
|
|
|
if res { winners += 1u };
|
|
|
|
}
|
|
|
|
assert winners == 1u;
|
|
|
|
}
|
|
|
|
}
|
2012-04-06 19:03:00 -05:00
|
|
|
|
2012-07-04 16:53:12 -05:00
|
|
|
/**
|
|
|
|
* Convert the current task to a 'weak' task temporarily
|
|
|
|
*
|
|
|
|
* As a weak task it will not be counted towards the runtime's set
|
|
|
|
* of live tasks. When there are no more outstanding live (non-weak) tasks
|
|
|
|
* the runtime will send an exit message on the provided channel.
|
|
|
|
*
|
|
|
|
* This function is super-unsafe. Do not use.
|
|
|
|
*
|
|
|
|
* # Safety notes
|
|
|
|
*
|
|
|
|
* * Weak tasks must either die on their own or exit upon receipt of
|
|
|
|
* the exit message. Failure to do so will cause the runtime to never
|
|
|
|
* exit
|
|
|
|
* * Tasks must not call `weaken_task` multiple times. This will
|
|
|
|
* break the kernel's accounting of live tasks.
|
|
|
|
* * Weak tasks must not be supervised. A supervised task keeps
|
|
|
|
* a reference to its parent, so the parent will not die.
|
|
|
|
*/
|
2012-08-15 16:10:46 -05:00
|
|
|
unsafe fn weaken_task(f: fn(comm::Port<()>)) {
|
2012-08-27 16:22:25 -05:00
|
|
|
let po = comm::Port();
|
|
|
|
let ch = comm::Chan(po);
|
2012-06-24 22:18:18 -05:00
|
|
|
unsafe {
|
|
|
|
rustrt::rust_task_weaken(unsafe::reinterpret_cast(ch));
|
|
|
|
}
|
2012-08-13 18:20:27 -05:00
|
|
|
let _unweaken = Unweaken(ch);
|
2012-04-06 19:03:00 -05:00
|
|
|
f(po);
|
|
|
|
|
2012-08-15 20:46:55 -05:00
|
|
|
struct Unweaken {
|
2012-08-15 16:10:46 -05:00
|
|
|
let ch: comm::Chan<()>;
|
|
|
|
new(ch: comm::Chan<()>) { self.ch = ch; }
|
2012-06-21 23:46:43 -05:00
|
|
|
drop unsafe {
|
|
|
|
rustrt::rust_task_unweaken(unsafe::reinterpret_cast(self.ch));
|
|
|
|
}
|
2012-04-06 19:03:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2012-06-24 22:18:18 -05:00
|
|
|
fn test_weaken_task_then_unweaken() {
|
2012-07-04 14:04:28 -05:00
|
|
|
do task::try {
|
2012-06-24 22:18:18 -05:00
|
|
|
unsafe {
|
2012-06-30 18:19:07 -05:00
|
|
|
do weaken_task |_po| {
|
2012-06-24 22:18:18 -05:00
|
|
|
}
|
2012-04-06 19:03:00 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2012-06-24 22:18:18 -05:00
|
|
|
fn test_weaken_task_wait() {
|
2012-07-23 14:53:18 -05:00
|
|
|
do task::spawn_unlinked {
|
2012-06-24 22:18:18 -05:00
|
|
|
unsafe {
|
2012-06-30 18:19:07 -05:00
|
|
|
do weaken_task |po| {
|
2012-06-24 22:18:18 -05:00
|
|
|
comm::recv(po);
|
|
|
|
}
|
2012-04-06 19:03:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2012-06-24 22:18:18 -05:00
|
|
|
fn test_weaken_task_stress() {
|
2012-04-06 19:03:00 -05:00
|
|
|
// Create a bunch of weak tasks
|
2012-07-04 14:04:28 -05:00
|
|
|
for iter::repeat(100u) {
|
|
|
|
do task::spawn {
|
2012-06-24 22:18:18 -05:00
|
|
|
unsafe {
|
2012-06-30 18:19:07 -05:00
|
|
|
do weaken_task |_po| {
|
2012-06-24 22:18:18 -05:00
|
|
|
}
|
2012-04-06 19:03:00 -05:00
|
|
|
}
|
|
|
|
}
|
2012-07-23 14:53:18 -05:00
|
|
|
do task::spawn_unlinked {
|
2012-06-24 22:18:18 -05:00
|
|
|
unsafe {
|
2012-06-30 18:19:07 -05:00
|
|
|
do weaken_task |po| {
|
2012-06-24 22:18:18 -05:00
|
|
|
// Wait for it to tell us to die
|
|
|
|
comm::recv(po);
|
|
|
|
}
|
2012-04-06 19:03:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2012-06-07 23:38:25 -05:00
|
|
|
#[ignore(cfg(windows))]
|
2012-06-24 22:18:18 -05:00
|
|
|
fn test_weaken_task_fail() {
|
2012-07-04 14:04:28 -05:00
|
|
|
let res = do task::try {
|
2012-06-24 22:18:18 -05:00
|
|
|
unsafe {
|
2012-06-30 18:19:07 -05:00
|
|
|
do weaken_task |_po| {
|
2012-06-24 22:18:18 -05:00
|
|
|
fail;
|
|
|
|
}
|
2012-04-06 19:03:00 -05:00
|
|
|
}
|
|
|
|
};
|
2012-06-22 20:26:25 -05:00
|
|
|
assert result::is_err(res);
|
2012-05-24 16:49:39 -05:00
|
|
|
}
|