2013-01-25 17:51:53 -08:00
|
|
|
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2013-01-15 19:53:35 -08:00
|
|
|
/*!
|
|
|
|
Weak tasks
|
|
|
|
|
|
|
|
Weak tasks are a runtime feature for building global services that
|
|
|
|
do not keep the runtime alive. Normally the runtime exits when all
|
|
|
|
tasks exits, but if a task is weak then the runtime may exit while
|
|
|
|
it is running, sending a notification to the task that the runtime
|
|
|
|
is trying to shut down.
|
|
|
|
*/
|
|
|
|
|
2013-02-25 13:23:16 -08:00
|
|
|
use cell::Cell;
|
|
|
|
use comm::{GenericSmartChan, stream};
|
|
|
|
use comm::{Port, Chan, SharedChan, GenericChan, GenericPort};
|
|
|
|
use hashmap::linear::LinearMap;
|
|
|
|
use ops::Drop;
|
2013-01-15 19:53:35 -08:00
|
|
|
use option::{Some, None, swap_unwrap};
|
|
|
|
use private::at_exit::at_exit;
|
|
|
|
use private::finally::Finally;
|
2013-02-25 13:23:16 -08:00
|
|
|
use private::global::global_data_clone_create;
|
2013-01-15 19:53:35 -08:00
|
|
|
use task::rt::{task_id, get_task_id};
|
2013-02-25 13:23:16 -08:00
|
|
|
use task::{Task, task, spawn};
|
2013-01-15 19:53:35 -08:00
|
|
|
|
|
|
|
type ShutdownMsg = ();
|
|
|
|
|
2013-01-31 19:34:46 -08:00
|
|
|
// FIXME #4729: This could be a PortOne but I've experienced bugginess
|
2013-01-15 19:53:35 -08:00
|
|
|
// with oneshot pipes and try_send
|
|
|
|
pub unsafe fn weaken_task(f: &fn(Port<ShutdownMsg>)) {
|
|
|
|
let service = global_data_clone_create(global_data_key,
|
|
|
|
create_global_service);
|
|
|
|
let (shutdown_port, shutdown_chan) = stream::<ShutdownMsg>();
|
2013-02-25 13:23:16 -08:00
|
|
|
let shutdown_port = Cell(shutdown_port);
|
2013-01-15 19:53:35 -08:00
|
|
|
let task = get_task_id();
|
|
|
|
// Expect the weak task service to be alive
|
|
|
|
assert service.try_send(RegisterWeakTask(task, shutdown_chan));
|
2013-02-12 12:27:19 +10:00
|
|
|
unsafe { rust_dec_kernel_live_count(); }
|
2013-01-15 19:53:35 -08:00
|
|
|
do fn&() {
|
2013-02-25 13:23:16 -08:00
|
|
|
f(shutdown_port.take())
|
2013-01-15 19:53:35 -08:00
|
|
|
}.finally || {
|
2013-02-12 12:27:19 +10:00
|
|
|
unsafe { rust_inc_kernel_live_count(); }
|
2013-01-15 19:53:35 -08:00
|
|
|
// Service my have already exited
|
|
|
|
service.send(UnregisterWeakTask(task));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type WeakTaskService = SharedChan<ServiceMsg>;
|
|
|
|
type TaskHandle = task_id;
|
|
|
|
|
|
|
|
fn global_data_key(_v: WeakTaskService) { }
|
|
|
|
|
|
|
|
enum ServiceMsg {
|
|
|
|
RegisterWeakTask(TaskHandle, Chan<ShutdownMsg>),
|
|
|
|
UnregisterWeakTask(TaskHandle),
|
|
|
|
Shutdown
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_global_service() -> ~WeakTaskService {
|
|
|
|
|
|
|
|
debug!("creating global weak task service");
|
|
|
|
let (port, chan) = stream::<ServiceMsg>();
|
2013-02-25 13:23:16 -08:00
|
|
|
let port = Cell(port);
|
2013-01-15 19:53:35 -08:00
|
|
|
let chan = SharedChan(chan);
|
|
|
|
let chan_clone = chan.clone();
|
|
|
|
|
|
|
|
do task().unlinked().spawn {
|
|
|
|
debug!("running global weak task service");
|
2013-02-25 13:23:16 -08:00
|
|
|
let port = Cell(port.take());
|
2013-01-15 19:53:35 -08:00
|
|
|
do fn&() {
|
2013-02-25 13:23:16 -08:00
|
|
|
let port = port.take();
|
2013-01-15 19:53:35 -08:00
|
|
|
// The weak task service is itself a weak task
|
|
|
|
debug!("weakening the weak service task");
|
2013-02-12 12:27:19 +10:00
|
|
|
unsafe { rust_dec_kernel_live_count(); }
|
2013-01-15 19:53:35 -08:00
|
|
|
run_weak_task_service(port);
|
|
|
|
}.finally {
|
|
|
|
debug!("unweakening the weak service task");
|
2013-02-12 12:27:19 +10:00
|
|
|
unsafe { rust_inc_kernel_live_count(); }
|
2013-01-15 19:53:35 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
do at_exit {
|
|
|
|
debug!("shutting down weak task service");
|
|
|
|
chan.send(Shutdown);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ~chan_clone;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run_weak_task_service(port: Port<ServiceMsg>) {
|
|
|
|
|
2013-01-25 17:51:53 -08:00
|
|
|
let mut shutdown_map = LinearMap::new();
|
2013-01-15 19:53:35 -08:00
|
|
|
|
|
|
|
loop {
|
|
|
|
match port.recv() {
|
|
|
|
RegisterWeakTask(task, shutdown_chan) => {
|
|
|
|
let previously_unregistered =
|
|
|
|
shutdown_map.insert(task, shutdown_chan);
|
|
|
|
assert previously_unregistered;
|
|
|
|
}
|
|
|
|
UnregisterWeakTask(task) => {
|
|
|
|
match shutdown_map.pop(&task) {
|
|
|
|
Some(shutdown_chan) => {
|
|
|
|
// Oneshot pipes must send, even though
|
|
|
|
// nobody will receive this
|
|
|
|
shutdown_chan.send(());
|
|
|
|
}
|
2013-02-11 19:26:38 -08:00
|
|
|
None => fail!()
|
2013-01-15 19:53:35 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Shutdown => break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
do shutdown_map.consume |_, shutdown_chan| {
|
|
|
|
// Weak task may have already exited
|
|
|
|
shutdown_chan.send(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern {
|
2013-02-09 19:19:31 +10:00
|
|
|
unsafe fn rust_inc_kernel_live_count();
|
|
|
|
unsafe fn rust_dec_kernel_live_count();
|
2013-01-15 19:53:35 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-25 17:51:53 -08:00
|
|
|
fn test_simple() {
|
2013-01-15 19:53:35 -08:00
|
|
|
let (port, chan) = stream();
|
2013-01-25 17:51:53 -08:00
|
|
|
do spawn {
|
|
|
|
unsafe {
|
|
|
|
do weaken_task |_signal| {
|
|
|
|
}
|
2013-01-15 19:53:35 -08:00
|
|
|
}
|
|
|
|
chan.send(());
|
|
|
|
}
|
|
|
|
port.recv();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-25 17:51:53 -08:00
|
|
|
fn test_weak_weak() {
|
2013-01-15 19:53:35 -08:00
|
|
|
let (port, chan) = stream();
|
2013-01-25 17:51:53 -08:00
|
|
|
do spawn {
|
|
|
|
unsafe {
|
|
|
|
do weaken_task |_signal| {
|
|
|
|
}
|
|
|
|
do weaken_task |_signal| {
|
|
|
|
}
|
2013-01-15 19:53:35 -08:00
|
|
|
}
|
|
|
|
chan.send(());
|
|
|
|
}
|
|
|
|
port.recv();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-25 17:51:53 -08:00
|
|
|
fn test_wait_for_signal() {
|
|
|
|
do spawn {
|
|
|
|
unsafe {
|
|
|
|
do weaken_task |signal| {
|
|
|
|
signal.recv();
|
|
|
|
}
|
2013-01-15 19:53:35 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-25 17:51:53 -08:00
|
|
|
fn test_wait_for_signal_many() {
|
2013-01-15 19:53:35 -08:00
|
|
|
use uint;
|
|
|
|
for uint::range(0, 100) |_| {
|
2013-01-25 17:51:53 -08:00
|
|
|
do spawn {
|
|
|
|
unsafe {
|
|
|
|
do weaken_task |signal| {
|
|
|
|
signal.recv();
|
|
|
|
}
|
2013-01-15 19:53:35 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-25 17:51:53 -08:00
|
|
|
fn test_select_stream_and_oneshot() {
|
2013-02-02 03:10:12 -08:00
|
|
|
use comm::select2i;
|
2013-01-15 19:53:35 -08:00
|
|
|
use either::{Left, Right};
|
|
|
|
|
|
|
|
let (port, chan) = stream();
|
|
|
|
let (waitport, waitchan) = stream();
|
2013-01-25 17:51:53 -08:00
|
|
|
do spawn {
|
|
|
|
unsafe {
|
|
|
|
do weaken_task |signal| {
|
|
|
|
match select2i(&port, &signal) {
|
|
|
|
Left(*) => (),
|
2013-02-11 19:26:38 -08:00
|
|
|
Right(*) => fail!()
|
2013-01-25 17:51:53 -08:00
|
|
|
}
|
2013-01-15 19:53:35 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
waitchan.send(());
|
|
|
|
}
|
|
|
|
chan.send(());
|
|
|
|
waitport.recv();
|
|
|
|
}
|
|
|
|
|