]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/common/helper_thread.rs
Imported Upstream version 1.0.0+dfsg1
[rustc.git] / src / libstd / sys / common / helper_thread.rs
CommitLineData
1a4d82fc
JJ
1// Copyright 2013-2014 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.
4//
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.
10
11//! Implementation of the helper thread for the timer module
12//!
13//! This module contains the management necessary for the timer worker thread.
14//! This thread is responsible for performing the send()s on channels for timers
15//! that are using channels instead of a blocking call.
16//!
17//! The timer thread is lazily initialized, and it's shut down via the
18//! `shutdown` function provided. It must be maintained as an invariant that
19//! `shutdown` is only called when the entire program is finished. No new timers
20//! can be created in the future and there must be no active timers at that
21//! time.
22
23use prelude::v1::*;
24
c34b1796 25use boxed;
1a4d82fc 26use cell::UnsafeCell;
1a4d82fc
JJ
27use rt;
28use sync::{StaticMutex, StaticCondvar};
29use sync::mpsc::{channel, Sender, Receiver};
30use sys::helper_signal;
31
85aaf69f 32use thread;
1a4d82fc
JJ
33
34/// A structure for management of a helper thread.
35///
36/// This is generally a static structure which tracks the lifetime of a helper
37/// thread.
38///
39/// The fields of this helper are all public, but they should not be used, this
40/// is for static initialization.
c34b1796 41pub struct Helper<M:Send> {
1a4d82fc
JJ
42 /// Internal lock which protects the remaining fields
43 pub lock: StaticMutex,
44 pub cond: StaticCondvar,
45
46 // You'll notice that the remaining fields are UnsafeCell<T>, and this is
47 // because all helper thread operations are done through &self, but we need
48 // these to be mutable (once `lock` is held).
49
50 /// Lazily allocated channel to send messages to the helper thread.
51 pub chan: UnsafeCell<*mut Sender<M>>,
52
53 /// OS handle used to wake up a blocked helper thread
c34b1796 54 pub signal: UnsafeCell<usize>,
1a4d82fc
JJ
55
56 /// Flag if this helper thread has booted and been initialized yet.
57 pub initialized: UnsafeCell<bool>,
58
59 /// Flag if this helper thread has shut down
60 pub shutdown: UnsafeCell<bool>,
61}
62
63unsafe impl<M:Send> Send for Helper<M> { }
64
65unsafe impl<M:Send> Sync for Helper<M> { }
66
67struct RaceBox(helper_signal::signal);
68
69unsafe impl Send for RaceBox {}
70unsafe impl Sync for RaceBox {}
71
c34b1796
AL
72macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => (
73 static $name: Helper<$m> = Helper {
74 lock: ::sync::MUTEX_INIT,
75 cond: ::sync::CONDVAR_INIT,
76 chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
77 signal: ::cell::UnsafeCell { value: 0 },
78 initialized: ::cell::UnsafeCell { value: false },
79 shutdown: ::cell::UnsafeCell { value: false },
80 };
81) }
82
1a4d82fc
JJ
83impl<M: Send> Helper<M> {
84 /// Lazily boots a helper thread, becoming a no-op if the helper has already
85 /// been spawned.
86 ///
87 /// This function will check to see if the thread has been initialized, and
88 /// if it has it returns quickly. If initialization has not happened yet,
89 /// the closure `f` will be run (inside of the initialization lock) and
bd371182 90 /// passed to the helper thread in a separate thread.
1a4d82fc
JJ
91 ///
92 /// This function is safe to be called many times.
93 pub fn boot<T, F>(&'static self, f: F, helper: fn(helper_signal::signal, Receiver<M>, T)) where
85aaf69f 94 T: Send + 'static,
1a4d82fc
JJ
95 F: FnOnce() -> T,
96 {
97 unsafe {
98 let _guard = self.lock.lock().unwrap();
c34b1796 99 if *self.chan.get() as usize == 0 {
1a4d82fc 100 let (tx, rx) = channel();
c34b1796 101 *self.chan.get() = boxed::into_raw(box tx);
1a4d82fc 102 let (receive, send) = helper_signal::new();
c34b1796 103 *self.signal.get() = send as usize;
1a4d82fc
JJ
104
105 let receive = RaceBox(receive);
106
107 let t = f();
85aaf69f 108 thread::spawn(move || {
1a4d82fc
JJ
109 helper(receive.0, rx, t);
110 let _g = self.lock.lock().unwrap();
111 *self.shutdown.get() = true;
112 self.cond.notify_one()
113 });
114
c34b1796 115 let _ = rt::at_exit(move || { self.shutdown() });
1a4d82fc 116 *self.initialized.get() = true;
c34b1796
AL
117 } else if *self.chan.get() as usize == 1 {
118 panic!("cannot continue usage after shutdown");
1a4d82fc
JJ
119 }
120 }
121 }
122
123 /// Sends a message to a spawned worker thread.
124 ///
125 /// This is only valid if the worker thread has previously booted
126 pub fn send(&'static self, msg: M) {
127 unsafe {
128 let _guard = self.lock.lock().unwrap();
129
130 // Must send and *then* signal to ensure that the child receives the
131 // message. Otherwise it could wake up and go to sleep before we
132 // send the message.
c34b1796
AL
133 assert!(*self.chan.get() as usize != 0);
134 assert!(*self.chan.get() as usize != 1,
135 "cannot continue usage after shutdown");
1a4d82fc
JJ
136 (**self.chan.get()).send(msg).unwrap();
137 helper_signal::signal(*self.signal.get() as helper_signal::signal);
138 }
139 }
140
141 fn shutdown(&'static self) {
142 unsafe {
143 // Shut down, but make sure this is done inside our lock to ensure
144 // that we'll always receive the exit signal when the thread
145 // returns.
146 let mut guard = self.lock.lock().unwrap();
147
c34b1796
AL
148 let ptr = *self.chan.get();
149 if ptr as usize == 1 {
150 panic!("cannot continue usage after shutdown");
151 }
1a4d82fc 152 // Close the channel by destroying it
c34b1796
AL
153 let chan = Box::from_raw(*self.chan.get());
154 *self.chan.get() = 1 as *mut Sender<M>;
1a4d82fc
JJ
155 drop(chan);
156 helper_signal::signal(*self.signal.get() as helper_signal::signal);
157
158 // Wait for the child to exit
159 while !*self.shutdown.get() {
160 guard = self.cond.wait(guard).unwrap();
161 }
162 drop(guard);
163
164 // Clean up after ourselves
165 self.lock.destroy();
166 helper_signal::close(*self.signal.get() as helper_signal::signal);
167 *self.signal.get() = 0;
168 }
169 }
170}