]> git.lizzy.rs Git - rust.git/blob - src/libnative/io/timer_helper.rs
auto merge of #13967 : richo/rust/features/ICE-fails, r=alexcrichton
[rust.git] / src / libnative / io / timer_helper.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 std::cast;
24 use std::rt::bookkeeping;
25 use std::rt;
26 use std::unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
27
28 use io::timer::{Req, Shutdown};
29 use task;
30
31 // You'll note that these variables are *not* protected by a lock. These
32 // variables are initialized with a Once before any Timer is created and are
33 // only torn down after everything else has exited. This means that these
34 // variables are read-only during use (after initialization) and both of which
35 // are safe to use concurrently.
36 static mut HELPER_CHAN: *mut Sender<Req> = 0 as *mut Sender<Req>;
37 static mut HELPER_SIGNAL: imp::signal = 0 as imp::signal;
38
39 static mut TIMER_HELPER_EXIT: StaticNativeMutex = NATIVE_MUTEX_INIT;
40
41 pub fn boot(helper: fn(imp::signal, Receiver<Req>)) {
42     static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
43     static mut INITIALIZED: bool = false;
44
45     unsafe {
46         let mut _guard = LOCK.lock();
47         if !INITIALIZED {
48             let (tx, rx) = channel();
49             // promote this to a shared channel
50             drop(tx.clone());
51             HELPER_CHAN = cast::transmute(box tx);
52             let (receive, send) = imp::new();
53             HELPER_SIGNAL = send;
54
55             task::spawn(proc() {
56                 bookkeeping::decrement();
57                 helper(receive, rx);
58                 TIMER_HELPER_EXIT.lock().signal()
59             });
60
61             rt::at_exit(proc() { shutdown() });
62             INITIALIZED = true;
63         }
64     }
65 }
66
67 pub fn send(req: Req) {
68     unsafe {
69         assert!(!HELPER_CHAN.is_null());
70         (*HELPER_CHAN).send(req);
71         imp::signal(HELPER_SIGNAL);
72     }
73 }
74
75 fn shutdown() {
76     // Request a shutdown, and then wait for the task to exit
77     unsafe {
78         let guard = TIMER_HELPER_EXIT.lock();
79         send(Shutdown);
80         guard.wait();
81         drop(guard);
82         TIMER_HELPER_EXIT.destroy();
83     }
84
85
86     // Clean up after ther helper thread
87     unsafe {
88         imp::close(HELPER_SIGNAL);
89         let _chan: Box<Sender<Req>> = cast::transmute(HELPER_CHAN);
90         HELPER_CHAN = 0 as *mut Sender<Req>;
91         HELPER_SIGNAL = 0 as imp::signal;
92     }
93 }
94
95 #[cfg(unix)]
96 mod imp {
97     use libc;
98     use std::os;
99
100     use io::file::FileDesc;
101
102     pub type signal = libc::c_int;
103
104     pub fn new() -> (signal, signal) {
105         let pipe = os::pipe();
106         (pipe.input, pipe.out)
107     }
108
109     pub fn signal(fd: libc::c_int) {
110         FileDesc::new(fd, false).inner_write([0]).unwrap();
111     }
112
113     pub fn close(fd: libc::c_int) {
114         let _fd = FileDesc::new(fd, true);
115     }
116 }
117
118 #[cfg(windows)]
119 mod imp {
120     use libc::{BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle};
121     use std::ptr;
122     use libc;
123
124     pub type signal = HANDLE;
125
126     pub fn new() -> (HANDLE, HANDLE) {
127         unsafe {
128             let handle = CreateEventA(ptr::mut_null(), libc::FALSE, libc::FALSE,
129                                       ptr::null());
130             (handle, handle)
131         }
132     }
133
134     pub fn signal(handle: HANDLE) {
135         assert!(unsafe { SetEvent(handle) != 0 });
136     }
137
138     pub fn close(handle: HANDLE) {
139         assert!(unsafe { CloseHandle(handle) != 0 });
140     }
141
142     extern "system" {
143         fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
144                         bManualReset: BOOL,
145                         bInitialState: BOOL,
146                         lpName: LPCSTR) -> HANDLE;
147         fn SetEvent(hEvent: HANDLE) -> BOOL;
148     }
149 }