]>
Commit | Line | Data |
---|---|---|
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 | ||
23 | use prelude::v1::*; | |
24 | ||
c34b1796 | 25 | use boxed; |
1a4d82fc | 26 | use cell::UnsafeCell; |
1a4d82fc JJ |
27 | use rt; |
28 | use sync::{StaticMutex, StaticCondvar}; | |
29 | use sync::mpsc::{channel, Sender, Receiver}; | |
30 | use sys::helper_signal; | |
31 | ||
85aaf69f | 32 | use 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 | 41 | pub 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 | ||
63 | unsafe impl<M:Send> Send for Helper<M> { } | |
64 | ||
65 | unsafe impl<M:Send> Sync for Helper<M> { } | |
66 | ||
67 | struct RaceBox(helper_signal::signal); | |
68 | ||
69 | unsafe impl Send for RaceBox {} | |
70 | unsafe impl Sync for RaceBox {} | |
71 | ||
c34b1796 AL |
72 | macro_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 |
83 | impl<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 | } |