]> git.lizzy.rs Git - rust.git/blob - src/librustuv/timer.rs
b940774323a2a57dae027e26afaf0fa07da7cfb0
[rust.git] / src / librustuv / timer.rs
1 // Copyright 2013 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 use std::mem;
12 use std::rt::rtio::{RtioTimer, Callback};
13 use std::rt::task::BlockedTask;
14
15 use homing::{HomeHandle, HomingIO};
16 use super::{UvHandle, ForbidUnwind, ForbidSwitch, wait_until_woken_after, Loop};
17 use uvio::UvIoFactory;
18 use uvll;
19
20 pub struct TimerWatcher {
21     pub handle: *uvll::uv_timer_t,
22     home: HomeHandle,
23     action: Option<NextAction>,
24     blocker: Option<BlockedTask>,
25     id: uint, // see comments in timer_cb
26 }
27
28 pub enum NextAction {
29     WakeTask,
30     CallOnce(Box<Callback:Send>),
31     CallMany(Box<Callback:Send>, uint),
32 }
33
34 impl TimerWatcher {
35     pub fn new(io: &mut UvIoFactory) -> Box<TimerWatcher> {
36         let handle = io.make_handle();
37         let me = box TimerWatcher::new_home(&io.loop_, handle);
38         me.install()
39     }
40
41     pub fn new_home(loop_: &Loop, home: HomeHandle) -> TimerWatcher {
42         let handle = UvHandle::alloc(None::<TimerWatcher>, uvll::UV_TIMER);
43         assert_eq!(unsafe { uvll::uv_timer_init(loop_.handle, handle) }, 0);
44         TimerWatcher {
45             handle: handle,
46             action: None,
47             blocker: None,
48             home: home,
49             id: 0,
50         }
51     }
52
53     pub fn start(&mut self, f: uvll::uv_timer_cb, msecs: u64, period: u64) {
54         assert_eq!(unsafe {
55             uvll::uv_timer_start(self.handle, f, msecs, period)
56         }, 0)
57     }
58
59     pub fn stop(&mut self) {
60         assert_eq!(unsafe { uvll::uv_timer_stop(self.handle) }, 0)
61     }
62
63     pub unsafe fn set_data<T>(&mut self, data: *T) {
64         uvll::set_data_for_uv_handle(self.handle, data);
65     }
66 }
67
68 impl HomingIO for TimerWatcher {
69     fn home<'r>(&'r mut self) -> &'r mut HomeHandle { &mut self.home }
70 }
71
72 impl UvHandle<uvll::uv_timer_t> for TimerWatcher {
73     fn uv_handle(&self) -> *uvll::uv_timer_t { self.handle }
74 }
75
76 impl RtioTimer for TimerWatcher {
77     fn sleep(&mut self, msecs: u64) {
78         // As with all of the below functions, we must be extra careful when
79         // destroying the previous action. If the previous action was a channel,
80         // destroying it could invoke a context switch. For these situtations,
81         // we must temporarily un-home ourselves, then destroy the action, and
82         // then re-home again.
83         let missile = self.fire_homing_missile();
84         self.id += 1;
85         self.stop();
86         let _missile = match mem::replace(&mut self.action, None) {
87             None => missile, // no need to do a homing dance
88             Some(action) => {
89                 drop(missile);      // un-home ourself
90                 drop(action);       // destroy the previous action
91                 self.fire_homing_missile()  // re-home ourself
92             }
93         };
94
95         // If the descheduling operation unwinds after the timer has been
96         // started, then we need to call stop on the timer.
97         let _f = ForbidUnwind::new("timer");
98
99         self.action = Some(WakeTask);
100         wait_until_woken_after(&mut self.blocker, &self.uv_loop(), || {
101             self.start(timer_cb, msecs, 0);
102         });
103         self.stop();
104     }
105
106     fn oneshot(&mut self, msecs: u64, cb: Box<Callback:Send>) {
107         // similarly to the destructor, we must drop the previous action outside
108         // of the homing missile
109         let _prev_action = {
110             let _m = self.fire_homing_missile();
111             self.id += 1;
112             self.stop();
113             self.start(timer_cb, msecs, 0);
114             mem::replace(&mut self.action, Some(CallOnce(cb)))
115         };
116     }
117
118     fn period(&mut self, msecs: u64, cb: Box<Callback:Send>) {
119         // similarly to the destructor, we must drop the previous action outside
120         // of the homing missile
121         let _prev_action = {
122             let _m = self.fire_homing_missile();
123             self.id += 1;
124             self.stop();
125             self.start(timer_cb, msecs, msecs);
126             mem::replace(&mut self.action, Some(CallMany(cb, self.id)))
127         };
128     }
129 }
130
131 extern fn timer_cb(handle: *uvll::uv_timer_t) {
132     let _f = ForbidSwitch::new("timer callback can't switch");
133     let timer: &mut TimerWatcher = unsafe { UvHandle::from_uv_handle(&handle) };
134
135     match timer.action.take_unwrap() {
136         WakeTask => {
137             let task = timer.blocker.take_unwrap();
138             let _ = task.wake().map(|t| t.reawaken());
139         }
140         CallOnce(mut cb) => { cb.call() }
141         CallMany(mut cb, id) => {
142             cb.call();
143
144             // Note that the above operation could have performed some form of
145             // scheduling. This means that the timer may have decided to insert
146             // some other action to happen. This 'id' keeps track of the updates
147             // to the timer, so we only reset the action back to sending on this
148             // channel if the id has remained the same. This is essentially a
149             // bug in that we have mutably aliasable memory, but that's libuv
150             // for you. We're guaranteed to all be running on the same thread,
151             // so there's no need for any synchronization here.
152             if timer.id == id {
153                 timer.action = Some(CallMany(cb, id));
154             }
155         }
156     }
157 }
158
159 impl Drop for TimerWatcher {
160     fn drop(&mut self) {
161         // note that this drop is a little subtle. Dropping a channel which is
162         // held internally may invoke some scheduling operations. We can't take
163         // the channel unless we're on the home scheduler, but once we're on the
164         // home scheduler we should never move. Hence, we take the timer's
165         // action item and then move it outside of the homing block.
166         let _action = {
167             let _m = self.fire_homing_missile();
168             self.stop();
169             self.close();
170             self.action.take()
171         };
172     }
173 }