1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
14 Weak tasks are a runtime feature for building global services that
15 do not keep the runtime alive. Normally the runtime exits when all
16 tasks exits, but if a task is weak then the runtime may exit while
17 it is running, sending a notification to the task that the runtime
18 is trying to shut down.
22 use comm::{GenericSmartChan, stream};
23 use comm::{Port, Chan, SharedChan, GenericChan, GenericPort};
25 use option::{Some, None};
26 use unstable::at_exit::at_exit;
27 use unstable::finally::Finally;
28 use unstable::global::global_data_clone_create;
29 use task::rt::{task_id, get_task_id};
32 #[cfg(test)] use task::spawn;
34 type ShutdownMsg = ();
36 // FIXME #4729: This could be a PortOne but I've experienced bugginess
37 // with oneshot pipes and try_send
38 pub unsafe fn weaken_task(f: &fn(Port<ShutdownMsg>)) {
39 let service = global_data_clone_create(global_data_key,
40 create_global_service);
41 let (shutdown_port, shutdown_chan) = stream::<ShutdownMsg>();
42 let shutdown_port = Cell::new(shutdown_port);
43 let task = get_task_id();
44 // Expect the weak task service to be alive
45 assert!(service.try_send(RegisterWeakTask(task, shutdown_chan)));
46 rust_dec_kernel_live_count();
48 f(shutdown_port.take())
50 rust_inc_kernel_live_count();
51 // Service my have already exited
52 service.send(UnregisterWeakTask(task));
56 type WeakTaskService = SharedChan<ServiceMsg>;
57 type TaskHandle = task_id;
59 fn global_data_key(_v: WeakTaskService) { }
62 RegisterWeakTask(TaskHandle, Chan<ShutdownMsg>),
63 UnregisterWeakTask(TaskHandle),
67 fn create_global_service() -> ~WeakTaskService {
69 debug!("creating global weak task service");
70 let (port, chan) = stream::<ServiceMsg>();
71 let port = Cell::new(port);
72 let chan = SharedChan::new(chan);
73 let chan_clone = chan.clone();
75 let mut task = task();
78 debug!("running global weak task service");
79 let port = Cell::new(port.take());
81 let port = port.take();
82 // The weak task service is itself a weak task
83 debug!("weakening the weak service task");
84 unsafe { rust_dec_kernel_live_count(); }
85 run_weak_task_service(port);
87 debug!("unweakening the weak service task");
88 unsafe { rust_inc_kernel_live_count(); }
93 debug!("shutting down weak task service");
100 fn run_weak_task_service(port: Port<ServiceMsg>) {
102 let mut shutdown_map = HashMap::new();
106 RegisterWeakTask(task, shutdown_chan) => {
107 let previously_unregistered =
108 shutdown_map.insert(task, shutdown_chan);
109 assert!(previously_unregistered);
111 UnregisterWeakTask(task) => {
112 match shutdown_map.pop(&task) {
113 Some(shutdown_chan) => {
114 // Oneshot pipes must send, even though
115 // nobody will receive this
116 shutdown_chan.send(());
125 for shutdown_map.consume().advance |(_, shutdown_chan)| {
126 // Weak task may have already exited
127 shutdown_chan.send(());
132 unsafe fn rust_inc_kernel_live_count();
133 unsafe fn rust_dec_kernel_live_count();
138 let (port, chan) = stream();
141 do weaken_task |_signal| {
150 fn test_weak_weak() {
151 let (port, chan) = stream();
154 do weaken_task |_signal| {
156 do weaken_task |_signal| {
165 fn test_wait_for_signal() {
168 do weaken_task |signal| {
176 fn test_wait_for_signal_many() {
178 for uint::range(0, 100) |_| {
181 do weaken_task |signal| {
190 fn test_select_stream_and_oneshot() {
192 use either::{Left, Right};
194 let (port, chan) = stream();
195 let port = Cell::new(port);
196 let (waitport, waitchan) = stream();
199 do weaken_task |mut signal| {
200 let mut port = port.take();
201 match select2i(&mut port, &mut signal) {