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