]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/common/helper_thread.rs
6f6179a436e914c3c77de9384be7e6b41799ea02
[rust.git] / src / libstd / sys / common / helper_thread.rs
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
25 use cell::UnsafeCell;
26 use mem;
27 use ptr;
28 use rt;
29 use sync::{StaticMutex, StaticCondvar};
30 use sync::mpsc::{channel, Sender, Receiver};
31 use sys::helper_signal;
32
33 use thread::Thread;
34
35 /// A structure for management of a helper thread.
36 ///
37 /// This is generally a static structure which tracks the lifetime of a helper
38 /// thread.
39 ///
40 /// The fields of this helper are all public, but they should not be used, this
41 /// is for static initialization.
42 pub struct Helper<M> {
43     /// Internal lock which protects the remaining fields
44     pub lock: StaticMutex,
45     pub cond: StaticCondvar,
46
47     // You'll notice that the remaining fields are UnsafeCell<T>, and this is
48     // because all helper thread operations are done through &self, but we need
49     // these to be mutable (once `lock` is held).
50
51     /// Lazily allocated channel to send messages to the helper thread.
52     pub chan: UnsafeCell<*mut Sender<M>>,
53
54     /// OS handle used to wake up a blocked helper thread
55     pub signal: UnsafeCell<uint>,
56
57     /// Flag if this helper thread has booted and been initialized yet.
58     pub initialized: UnsafeCell<bool>,
59
60     /// Flag if this helper thread has shut down
61     pub shutdown: UnsafeCell<bool>,
62 }
63
64 unsafe impl<M:Send> Send for Helper<M> { }
65
66 unsafe impl<M:Send> Sync for Helper<M> { }
67
68 struct RaceBox(helper_signal::signal);
69
70 unsafe impl Send for RaceBox {}
71 unsafe impl Sync for RaceBox {}
72
73 impl<M: Send> Helper<M> {
74     /// Lazily boots a helper thread, becoming a no-op if the helper has already
75     /// been spawned.
76     ///
77     /// This function will check to see if the thread has been initialized, and
78     /// if it has it returns quickly. If initialization has not happened yet,
79     /// the closure `f` will be run (inside of the initialization lock) and
80     /// passed to the helper thread in a separate task.
81     ///
82     /// This function is safe to be called many times.
83     pub fn boot<T, F>(&'static self, f: F, helper: fn(helper_signal::signal, Receiver<M>, T)) where
84         T: Send,
85         F: FnOnce() -> T,
86     {
87         unsafe {
88             let _guard = self.lock.lock().unwrap();
89             if !*self.initialized.get() {
90                 let (tx, rx) = channel();
91                 *self.chan.get() = mem::transmute(box tx);
92                 let (receive, send) = helper_signal::new();
93                 *self.signal.get() = send as uint;
94
95                 let receive = RaceBox(receive);
96
97                 let t = f();
98                 Thread::spawn(move |:| {
99                     helper(receive.0, rx, t);
100                     let _g = self.lock.lock().unwrap();
101                     *self.shutdown.get() = true;
102                     self.cond.notify_one()
103                 });
104
105                 rt::at_exit(move|:| { self.shutdown() });
106                 *self.initialized.get() = true;
107             }
108         }
109     }
110
111     /// Sends a message to a spawned worker thread.
112     ///
113     /// This is only valid if the worker thread has previously booted
114     pub fn send(&'static self, msg: M) {
115         unsafe {
116             let _guard = self.lock.lock().unwrap();
117
118             // Must send and *then* signal to ensure that the child receives the
119             // message. Otherwise it could wake up and go to sleep before we
120             // send the message.
121             assert!(!self.chan.get().is_null());
122             (**self.chan.get()).send(msg).unwrap();
123             helper_signal::signal(*self.signal.get() as helper_signal::signal);
124         }
125     }
126
127     fn shutdown(&'static self) {
128         unsafe {
129             // Shut down, but make sure this is done inside our lock to ensure
130             // that we'll always receive the exit signal when the thread
131             // returns.
132             let mut guard = self.lock.lock().unwrap();
133
134             // Close the channel by destroying it
135             let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
136             *self.chan.get() = ptr::null_mut();
137             drop(chan);
138             helper_signal::signal(*self.signal.get() as helper_signal::signal);
139
140             // Wait for the child to exit
141             while !*self.shutdown.get() {
142                 guard = self.cond.wait(guard).unwrap();
143             }
144             drop(guard);
145
146             // Clean up after ourselves
147             self.lock.destroy();
148             helper_signal::close(*self.signal.get() as helper_signal::signal);
149             *self.signal.get() = 0;
150         }
151     }
152 }