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.
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.
11 //! Implementation of the helper thread for the timer module
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.
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
26 use std::rt::bookkeeping;
27 use std::rt::mutex::StaticNativeMutex;
29 use std::cell::UnsafeCell;
33 /// A structure for management of a helper thread.
35 /// This is generally a static structure which tracks the lifetime of a helper
38 /// The fields of this helper are all public, but they should not be used, this
39 /// is for static initialization.
40 pub struct Helper<M> {
41 /// Internal lock which protects the remaining fields
42 pub lock: StaticNativeMutex,
44 // You'll notice that the remaining fields are UnsafeCell<T>, and this is
45 // because all helper thread operations are done through &self, but we need
46 // these to be mutable (once `lock` is held).
48 /// Lazily allocated channel to send messages to the helper thread.
49 pub chan: UnsafeCell<*mut Sender<M>>,
51 /// OS handle used to wake up a blocked helper thread
52 pub signal: UnsafeCell<uint>,
54 /// Flag if this helper thread has booted and been initialized yet.
55 pub initialized: UnsafeCell<bool>,
58 macro_rules! helper_init( (static mut $name:ident: Helper<$m:ty>) => (
59 static mut $name: Helper<$m> = Helper {
60 lock: ::std::rt::mutex::NATIVE_MUTEX_INIT,
61 chan: ::std::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
62 signal: ::std::cell::UnsafeCell { value: 0 },
63 initialized: ::std::cell::UnsafeCell { value: false },
67 impl<M: Send> Helper<M> {
68 /// Lazily boots a helper thread, becoming a no-op if the helper has already
71 /// This function will check to see if the thread has been initialized, and
72 /// if it has it returns quickly. If initialization has not happened yet,
73 /// the closure `f` will be run (inside of the initialization lock) and
74 /// passed to the helper thread in a separate task.
76 /// This function is safe to be called many times.
77 pub fn boot<T: Send>(&'static self,
79 helper: fn(imp::signal, Receiver<M>, T)) {
81 let _guard = self.lock.lock();
82 if !*self.initialized.get() {
83 let (tx, rx) = channel();
84 *self.chan.get() = mem::transmute(box tx);
85 let (receive, send) = imp::new();
86 *self.signal.get() = send as uint;
90 bookkeeping::decrement();
91 helper(receive, rx, t);
92 self.lock.lock().signal()
95 rt::at_exit(proc() { self.shutdown() });
96 *self.initialized.get() = true;
101 /// Sends a message to a spawned worker thread.
103 /// This is only valid if the worker thread has previously booted
104 pub fn send(&'static self, msg: M) {
106 let _guard = self.lock.lock();
108 // Must send and *then* signal to ensure that the child receives the
109 // message. Otherwise it could wake up and go to sleep before we
111 assert!(!self.chan.get().is_null());
112 (**self.chan.get()).send(msg);
113 imp::signal(*self.signal.get() as imp::signal);
117 fn shutdown(&'static self) {
119 // Shut down, but make sure this is done inside our lock to ensure
120 // that we'll always receive the exit signal when the thread
122 let guard = self.lock.lock();
124 // Close the channel by destroying it
125 let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
126 *self.chan.get() = 0 as *mut Sender<M>;
128 imp::signal(*self.signal.get() as imp::signal);
130 // Wait for the child to exit
134 // Clean up after ourselves
136 imp::close(*self.signal.get() as imp::signal);
137 *self.signal.get() = 0;
147 use io::file::FileDesc;
149 pub type signal = libc::c_int;
151 pub fn new() -> (signal, signal) {
152 let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
156 pub fn signal(fd: libc::c_int) {
157 FileDesc::new(fd, false).inner_write([0]).ok().unwrap();
160 pub fn close(fd: libc::c_int) {
161 let _fd = FileDesc::new(fd, true);
167 use libc::{BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle};
171 pub type signal = HANDLE;
173 pub fn new() -> (HANDLE, HANDLE) {
175 let handle = CreateEventA(ptr::null_mut(), libc::FALSE, libc::FALSE,
181 pub fn signal(handle: HANDLE) {
182 assert!(unsafe { SetEvent(handle) != 0 });
185 pub fn close(handle: HANDLE) {
186 assert!(unsafe { CloseHandle(handle) != 0 });
190 fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
193 lpName: LPCSTR) -> HANDLE;
194 fn SetEvent(hEvent: HANDLE) -> BOOL;