]> git.lizzy.rs Git - rust.git/blob - src/librustuv/idle.rs
Rename all raw pointers as necessary
[rust.git] / src / librustuv / idle.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 libc::c_void;
12 use std::mem;
13
14 use uvll;
15 use super::{Loop, UvHandle};
16 use std::rt::rtio::{Callback, PausableIdleCallback};
17
18 pub struct IdleWatcher {
19     handle: *mut uvll::uv_idle_t,
20     idle_flag: bool,
21     callback: Box<Callback + Send>,
22 }
23
24 impl IdleWatcher {
25     pub fn new(loop_: &mut Loop, cb: Box<Callback + Send>) -> Box<IdleWatcher> {
26         let handle = UvHandle::alloc(None::<IdleWatcher>, uvll::UV_IDLE);
27         assert_eq!(unsafe {
28             uvll::uv_idle_init(loop_.handle, handle)
29         }, 0);
30         let me = box IdleWatcher {
31             handle: handle,
32             idle_flag: false,
33             callback: cb,
34         };
35         return me.install();
36     }
37
38     pub fn onetime(loop_: &mut Loop, f: proc()) {
39         let handle = UvHandle::alloc(None::<IdleWatcher>, uvll::UV_IDLE);
40         unsafe {
41             assert_eq!(uvll::uv_idle_init(loop_.handle, handle), 0);
42             let data: *mut c_void = mem::transmute(box f);
43             uvll::set_data_for_uv_handle(handle, data);
44             assert_eq!(uvll::uv_idle_start(handle, onetime_cb), 0)
45         }
46
47         extern fn onetime_cb(handle: *mut uvll::uv_idle_t) {
48             unsafe {
49                 let data = uvll::get_data_for_uv_handle(handle);
50                 let f: Box<proc()> = mem::transmute(data);
51                 (*f)();
52                 assert_eq!(uvll::uv_idle_stop(handle), 0);
53                 uvll::uv_close(handle, close_cb);
54             }
55         }
56
57         extern fn close_cb(handle: *mut uvll::uv_handle_t) {
58             unsafe { uvll::free_handle(handle) }
59         }
60     }
61 }
62
63 impl PausableIdleCallback for IdleWatcher {
64     fn pause(&mut self) {
65         if self.idle_flag == true {
66             assert_eq!(unsafe {uvll::uv_idle_stop(self.handle) }, 0);
67             self.idle_flag = false;
68         }
69     }
70     fn resume(&mut self) {
71         if self.idle_flag == false {
72             assert_eq!(unsafe { uvll::uv_idle_start(self.handle, idle_cb) }, 0)
73             self.idle_flag = true;
74         }
75     }
76 }
77
78 impl UvHandle<uvll::uv_idle_t> for IdleWatcher {
79     fn uv_handle(&self) -> *mut uvll::uv_idle_t { self.handle }
80 }
81
82 extern fn idle_cb(handle: *mut uvll::uv_idle_t) {
83     let idle: &mut IdleWatcher = unsafe { UvHandle::from_uv_handle(&handle) };
84     idle.callback.call();
85 }
86
87 impl Drop for IdleWatcher {
88     fn drop(&mut self) {
89         self.pause();
90         self.close_async_();
91     }
92 }
93
94 #[cfg(test)]
95 mod test {
96     use std::mem;
97     use std::cell::RefCell;
98     use std::rc::Rc;
99     use std::rt::rtio::{Callback, PausableIdleCallback};
100     use std::rt::task::{BlockedTask, Task};
101     use std::rt::local::Local;
102     use super::IdleWatcher;
103     use super::super::local_loop;
104
105     type Chan = Rc<RefCell<(Option<BlockedTask>, uint)>>;
106
107     struct MyCallback(Rc<RefCell<(Option<BlockedTask>, uint)>>, uint);
108     impl Callback for MyCallback {
109         fn call(&mut self) {
110             let task = match *self {
111                 MyCallback(ref rc, n) => {
112                     match *rc.borrow_mut().deref_mut() {
113                         (ref mut task, ref mut val) => {
114                             *val = n;
115                             match task.take() {
116                                 Some(t) => t,
117                                 None => return
118                             }
119                         }
120                     }
121                 }
122             };
123             let _ = task.wake().map(|t| t.reawaken());
124         }
125     }
126
127     fn mk(v: uint) -> (Box<IdleWatcher>, Chan) {
128         let rc = Rc::new(RefCell::new((None, 0)));
129         let cb = box MyCallback(rc.clone(), v);
130         let cb = cb as Box<Callback>;
131         let cb = unsafe { mem::transmute(cb) };
132         (IdleWatcher::new(&mut local_loop().loop_, cb), rc)
133     }
134
135     fn sleep(chan: &Chan) -> uint {
136         let task: Box<Task> = Local::take();
137         task.deschedule(1, |task| {
138             match *chan.borrow_mut().deref_mut() {
139                 (ref mut slot, _) => {
140                     assert!(slot.is_none());
141                     *slot = Some(task);
142                 }
143             }
144             Ok(())
145         });
146
147         match *chan.borrow() { (_, n) => n }
148     }
149
150     #[test]
151     fn not_used() {
152         let (_idle, _chan) = mk(1);
153     }
154
155     #[test]
156     fn smoke_test() {
157         let (mut idle, chan) = mk(1);
158         idle.resume();
159         assert_eq!(sleep(&chan), 1);
160     }
161
162     #[test] #[should_fail]
163     fn smoke_fail() {
164         // By default, the test harness is capturing our stderr output through a
165         // channel. This means that when we start failing and "print" our error
166         // message, we could be switched to running on another test. The
167         // IdleWatcher assumes that we're already running on the same task, so
168         // it can cause serious problems and internal race conditions.
169         //
170         // To fix this bug, we just set our stderr to a null writer which will
171         // never reschedule us, so we're guaranteed to stay on the same
172         // task/event loop.
173         use std::io;
174         drop(io::stdio::set_stderr(box io::util::NullWriter));
175
176         let (mut idle, _chan) = mk(1);
177         idle.resume();
178         fail!();
179     }
180
181     #[test]
182     fn fun_combinations_of_methods() {
183         let (mut idle, chan) = mk(1);
184         idle.resume();
185         assert_eq!(sleep(&chan), 1);
186         idle.pause();
187         idle.resume();
188         idle.resume();
189         assert_eq!(sleep(&chan), 1);
190         idle.pause();
191         idle.pause();
192         idle.resume();
193         assert_eq!(sleep(&chan), 1);
194     }
195
196     #[test]
197     fn pause_pauses() {
198         let (mut idle1, chan1) = mk(1);
199         let (mut idle2, chan2) = mk(2);
200         idle2.resume();
201         assert_eq!(sleep(&chan2), 2);
202         idle2.pause();
203         idle1.resume();
204         assert_eq!(sleep(&chan1), 1);
205     }
206 }