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