]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/test.rs
Fix bug in `match`ing struct patterns
[rust.git] / src / libstd / rt / test.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;
12 use uint;
13 use option::{Some, None};
14 use cell::Cell;
15 use clone::Clone;
16 use container::Container;
17 use iterator::{Iterator, range};
18 use vec::{OwnedVector, MutableVector};
19 use super::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
20 use rt::sched::Scheduler;
21 use unstable::run_in_bare_thread;
22 use rt::thread::Thread;
23 use rt::task::Task;
24 use rt::uv::uvio::UvEventLoop;
25 use rt::work_queue::WorkQueue;
26 use rt::sleeper_list::SleeperList;
27 use rt::comm::oneshot;
28 use result::{Result, Ok, Err};
29
30 pub fn new_test_uv_sched() -> Scheduler {
31
32     let mut sched = Scheduler::new(~UvEventLoop::new(),
33                                    WorkQueue::new(),
34                                    SleeperList::new());
35
36     // Don't wait for the Shutdown message
37     sched.no_sleep = true;
38     return sched;
39
40 }
41
42 pub fn run_in_newsched_task(f: ~fn()) {
43     let f = Cell::new(f);
44     do run_in_bare_thread {
45         run_in_newsched_task_core(f.take());
46     }
47 }
48
49 pub fn run_in_newsched_task_core(f: ~fn()) {
50
51     use rt::sched::Shutdown;
52
53     let mut sched = ~new_test_uv_sched();
54     let exit_handle = Cell::new(sched.make_handle());
55
56     let on_exit: ~fn(bool) = |exit_status| {
57         exit_handle.take().send(Shutdown);
58         rtassert!(exit_status);
59     };
60     let mut task = ~Task::new_root(&mut sched.stack_pool, f);
61     task.death.on_exit = Some(on_exit);
62
63     sched.bootstrap(task);
64 }
65
66 #[cfg(target_os="macos")]
67 #[allow(non_camel_case_types)]
68 mod darwin_fd_limit {
69     /*!
70      * darwin_fd_limit exists to work around an issue where launchctl on Mac OS X defaults the
71      * rlimit maxfiles to 256/unlimited. The default soft limit of 256 ends up being far too low
72      * for our multithreaded scheduler testing, depending on the number of cores available.
73      *
74      * This fixes issue #7772.
75      */
76
77     use libc;
78     type rlim_t = libc::uint64_t;
79     struct rlimit {
80         rlim_cur: rlim_t,
81         rlim_max: rlim_t
82     }
83     #[nolink]
84     extern {
85         // name probably doesn't need to be mut, but the C function doesn't specify const
86         fn sysctl(name: *mut libc::c_int, namelen: libc::c_uint,
87                   oldp: *mut libc::c_void, oldlenp: *mut libc::size_t,
88                   newp: *mut libc::c_void, newlen: libc::size_t) -> libc::c_int;
89         fn getrlimit(resource: libc::c_int, rlp: *mut rlimit) -> libc::c_int;
90         fn setrlimit(resource: libc::c_int, rlp: *rlimit) -> libc::c_int;
91     }
92     static CTL_KERN: libc::c_int = 1;
93     static KERN_MAXFILESPERPROC: libc::c_int = 29;
94     static RLIMIT_NOFILE: libc::c_int = 8;
95
96     pub unsafe fn raise_fd_limit() {
97         // The strategy here is to fetch the current resource limits, read the kern.maxfilesperproc
98         // sysctl value, and bump the soft resource limit for maxfiles up to the sysctl value.
99         use ptr::{to_unsafe_ptr, to_mut_unsafe_ptr, mut_null};
100         use sys::size_of_val;
101         use os::last_os_error;
102
103         // Fetch the kern.maxfilesperproc value
104         let mut mib: [libc::c_int, ..2] = [CTL_KERN, KERN_MAXFILESPERPROC];
105         let mut maxfiles: libc::c_int = 0;
106         let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t;
107         if sysctl(to_mut_unsafe_ptr(&mut mib[0]), 2,
108                   to_mut_unsafe_ptr(&mut maxfiles) as *mut libc::c_void,
109                   to_mut_unsafe_ptr(&mut size),
110                   mut_null(), 0) != 0 {
111             let err = last_os_error();
112             error!("raise_fd_limit: error calling sysctl: %s", err);
113             return;
114         }
115
116         // Fetch the current resource limits
117         let mut rlim = rlimit{rlim_cur: 0, rlim_max: 0};
118         if getrlimit(RLIMIT_NOFILE, to_mut_unsafe_ptr(&mut rlim)) != 0 {
119             let err = last_os_error();
120             error!("raise_fd_limit: error calling getrlimit: %s", err);
121             return;
122         }
123
124         // Bump the soft limit to the smaller of kern.maxfilesperproc and the hard limit
125         rlim.rlim_cur = ::cmp::min(maxfiles as rlim_t, rlim.rlim_max);
126
127         // Set our newly-increased resource limit
128         if setrlimit(RLIMIT_NOFILE, to_unsafe_ptr(&rlim)) != 0 {
129             let err = last_os_error();
130             error!("raise_fd_limit: error calling setrlimit: %s", err);
131             return;
132         }
133     }
134 }
135
136 #[cfg(not(target_os="macos"))]
137 mod darwin_fd_limit {
138     pub unsafe fn raise_fd_limit() {}
139 }
140
141 /// Create more than one scheduler and run a function in a task
142 /// in one of the schedulers. The schedulers will stay alive
143 /// until the function `f` returns.
144 pub fn run_in_mt_newsched_task(f: ~fn()) {
145     use os;
146     use from_str::FromStr;
147     use rt::sched::Shutdown;
148     use rt::util;
149
150     // Bump the fd limit on OS X. See darwin_fd_limit for an explanation.
151     unsafe { darwin_fd_limit::raise_fd_limit() }
152
153     let f = Cell::new(f);
154
155     do run_in_bare_thread {
156         let nthreads = match os::getenv("RUST_RT_TEST_THREADS") {
157             Some(nstr) => FromStr::from_str(nstr).unwrap(),
158             None => {
159                 // Using more threads than cores in test code
160                 // to force the OS to preempt them frequently.
161                 // Assuming that this help stress test concurrent types.
162                 util::num_cpus() * 2
163             }
164         };
165
166         let sleepers = SleeperList::new();
167         let work_queue = WorkQueue::new();
168
169         let mut handles = ~[];
170         let mut scheds = ~[];
171
172         for _ in range(0u, nthreads) {
173             let loop_ = ~UvEventLoop::new();
174             let mut sched = ~Scheduler::new(loop_,
175                                             work_queue.clone(),
176                                             sleepers.clone());
177             let handle = sched.make_handle();
178
179             handles.push(handle);
180             scheds.push(sched);
181         }
182
183         let handles = Cell::new(handles);
184         let on_exit: ~fn(bool) = |exit_status| {
185             let mut handles = handles.take();
186             // Tell schedulers to exit
187             for handle in handles.mut_iter() {
188                 handle.send(Shutdown);
189             }
190
191             rtassert!(exit_status);
192         };
193         let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool,
194                                         f.take());
195         main_task.death.on_exit = Some(on_exit);
196
197         let mut threads = ~[];
198         let main_task = Cell::new(main_task);
199
200         let main_thread = {
201             let sched = scheds.pop();
202             let sched_cell = Cell::new(sched);
203             do Thread::start {
204                 let sched = sched_cell.take();
205                 sched.bootstrap(main_task.take());
206             }
207         };
208         threads.push(main_thread);
209
210         while !scheds.is_empty() {
211             let mut sched = scheds.pop();
212             let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool) || {
213                 rtdebug!("bootstrapping non-primary scheduler");
214             };
215             let bootstrap_task_cell = Cell::new(bootstrap_task);
216             let sched_cell = Cell::new(sched);
217             let thread = do Thread::start {
218                 let sched = sched_cell.take();
219                 sched.bootstrap(bootstrap_task_cell.take());
220             };
221
222             threads.push(thread);
223         }
224
225         // Wait for schedulers
226         for thread in threads.consume_iter() {
227             thread.join();
228         }
229     }
230
231 }
232
233 /// Test tasks will abort on failure instead of unwinding
234 pub fn spawntask(f: ~fn()) {
235     Scheduler::run_task(Task::build_child(f));
236 }
237
238 /// Create a new task and run it right now. Aborts on failure
239 pub fn spawntask_later(f: ~fn()) {
240     Scheduler::run_task_later(Task::build_child(f));
241 }
242
243 pub fn spawntask_random(f: ~fn()) {
244     use rand::{Rand, rng};
245
246     let mut rng = rng();
247     let run_now: bool = Rand::rand(&mut rng);
248
249     if run_now {
250         spawntask(f)
251     } else {
252         spawntask_later(f)
253     }
254 }
255
256 pub fn spawntask_try(f: ~fn()) -> Result<(),()> {
257
258     let (port, chan) = oneshot();
259     let chan = Cell::new(chan);
260     let on_exit: ~fn(bool) = |exit_status| chan.take().send(exit_status);
261
262     let mut new_task = Task::build_root(f);
263     new_task.death.on_exit = Some(on_exit);
264
265     Scheduler::run_task(new_task);
266
267     let exit_status = port.recv();
268     if exit_status { Ok(()) } else { Err(()) }
269
270 }
271
272 /// Spawn a new task in a new scheduler and return a thread handle.
273 pub fn spawntask_thread(f: ~fn()) -> Thread {
274
275     let f = Cell::new(f);
276
277     let thread = do Thread::start {
278         run_in_newsched_task_core(f.take());
279     };
280
281     return thread;
282 }
283
284 /// Get a ~Task for testing purposes other than actually scheduling it.
285 pub fn with_test_task(blk: ~fn(~Task) -> ~Task) {
286     do run_in_bare_thread {
287         let mut sched = ~new_test_uv_sched();
288         let task = blk(~Task::new_root(&mut sched.stack_pool, ||{}));
289         cleanup_task(task);
290     }
291 }
292
293 /// Use to cleanup tasks created for testing but not "run".
294 pub fn cleanup_task(mut task: ~Task) {
295     task.destroyed = true;
296 }
297
298 /// Get a port number, starting at 9600, for use in tests
299 pub fn next_test_port() -> u16 {
300     unsafe {
301         return rust_dbg_next_port(base_port() as libc::uintptr_t) as u16;
302     }
303     extern {
304         fn rust_dbg_next_port(base: libc::uintptr_t) -> libc::uintptr_t;
305     }
306 }
307
308 /// Get a unique IPv4 localhost:port pair starting at 9600
309 pub fn next_test_ip4() -> SocketAddr {
310     SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: next_test_port() }
311 }
312
313 /// Get a unique IPv6 localhost:port pair starting at 9600
314 pub fn next_test_ip6() -> SocketAddr {
315     SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1), port: next_test_port() }
316 }
317
318 /*
319 XXX: Welcome to MegaHack City.
320
321 The bots run multiple builds at the same time, and these builds
322 all want to use ports. This function figures out which workspace
323 it is running in and assigns a port range based on it.
324 */
325 fn base_port() -> uint {
326     use os;
327     use str::StrSlice;
328     use to_str::ToStr;
329     use vec::ImmutableVector;
330
331     let base = 9600u;
332     let range = 1000;
333
334     let bases = [
335         ("32-opt", base + range * 1),
336         ("32-noopt", base + range * 2),
337         ("64-opt", base + range * 3),
338         ("64-noopt", base + range * 4),
339         ("64-opt-vg", base + range * 5),
340         ("all-opt", base + range * 6),
341         ("snap3", base + range * 7),
342         ("dist", base + range * 8)
343     ];
344
345     let path = os::getcwd().to_str();
346
347     let mut final_base = base;
348
349     for &(dir, base) in bases.iter() {
350         if path.contains(dir) {
351             final_base = base;
352             break;
353         }
354     }
355
356     return final_base;
357 }
358
359 /// Get a constant that represents the number of times to repeat
360 /// stress tests. Default 1.
361 pub fn stress_factor() -> uint {
362     use os::getenv;
363
364     match getenv("RUST_RT_STRESS") {
365         Some(val) => uint::from_str(val).unwrap(),
366         None => 1
367     }
368 }