]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/test.rs
792ea5eb33f5acfa138be4692e53222858c34c2a
[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, None, 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, None, f.take());
194         main_task.death.on_exit = Some(on_exit);
195
196         let mut threads = ~[];
197         let main_task = Cell::new(main_task);
198
199         let main_thread = {
200             let sched = scheds.pop();
201             let sched_cell = Cell::new(sched);
202             do Thread::start {
203                 let sched = sched_cell.take();
204                 sched.bootstrap(main_task.take());
205             }
206         };
207         threads.push(main_thread);
208
209         while !scheds.is_empty() {
210             let mut sched = scheds.pop();
211             let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool, None) || {
212                 rtdebug!("bootstrapping non-primary scheduler");
213             };
214             let bootstrap_task_cell = Cell::new(bootstrap_task);
215             let sched_cell = Cell::new(sched);
216             let thread = do Thread::start {
217                 let sched = sched_cell.take();
218                 sched.bootstrap(bootstrap_task_cell.take());
219             };
220
221             threads.push(thread);
222         }
223
224         // Wait for schedulers
225         for thread in threads.consume_iter() {
226             thread.join();
227         }
228     }
229
230 }
231
232 /// Test tasks will abort on failure instead of unwinding
233 pub fn spawntask(f: ~fn()) {
234     Scheduler::run_task(Task::build_child(None, f));
235 }
236
237 /// Create a new task and run it right now. Aborts on failure
238 pub fn spawntask_later(f: ~fn()) {
239     Scheduler::run_task_later(Task::build_child(None, f));
240 }
241
242 pub fn spawntask_random(f: ~fn()) {
243     use rand::{Rand, rng};
244
245     let mut rng = rng();
246     let run_now: bool = Rand::rand(&mut rng);
247
248     if run_now {
249         spawntask(f)
250     } else {
251         spawntask_later(f)
252     }
253 }
254
255 pub fn spawntask_try(f: ~fn()) -> Result<(),()> {
256
257     let (port, chan) = oneshot();
258     let chan = Cell::new(chan);
259     let on_exit: ~fn(bool) = |exit_status| chan.take().send(exit_status);
260
261     let mut new_task = Task::build_root(None, f);
262     new_task.death.on_exit = Some(on_exit);
263
264     Scheduler::run_task(new_task);
265
266     let exit_status = port.recv();
267     if exit_status { Ok(()) } else { Err(()) }
268
269 }
270
271 /// Spawn a new task in a new scheduler and return a thread handle.
272 pub fn spawntask_thread(f: ~fn()) -> Thread {
273
274     let f = Cell::new(f);
275
276     let thread = do Thread::start {
277         run_in_newsched_task_core(f.take());
278     };
279
280     return thread;
281 }
282
283 /// Get a ~Task for testing purposes other than actually scheduling it.
284 pub fn with_test_task(blk: ~fn(~Task) -> ~Task) {
285     do run_in_bare_thread {
286         let mut sched = ~new_test_uv_sched();
287         let task = blk(~Task::new_root(&mut sched.stack_pool, None, ||{}));
288         cleanup_task(task);
289     }
290 }
291
292 /// Use to cleanup tasks created for testing but not "run".
293 pub fn cleanup_task(mut task: ~Task) {
294     task.destroyed = true;
295 }
296
297 /// Get a port number, starting at 9600, for use in tests
298 pub fn next_test_port() -> u16 {
299     unsafe {
300         return rust_dbg_next_port(base_port() as libc::uintptr_t) as u16;
301     }
302     extern {
303         fn rust_dbg_next_port(base: libc::uintptr_t) -> libc::uintptr_t;
304     }
305 }
306
307 /// Get a unique IPv4 localhost:port pair starting at 9600
308 pub fn next_test_ip4() -> SocketAddr {
309     SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: next_test_port() }
310 }
311
312 /// Get a unique IPv6 localhost:port pair starting at 9600
313 pub fn next_test_ip6() -> SocketAddr {
314     SocketAddr { ip: Ipv6Addr(0, 0, 0, 0, 0, 0, 0, 1), port: next_test_port() }
315 }
316
317 /*
318 XXX: Welcome to MegaHack City.
319
320 The bots run multiple builds at the same time, and these builds
321 all want to use ports. This function figures out which workspace
322 it is running in and assigns a port range based on it.
323 */
324 fn base_port() -> uint {
325     use os;
326     use str::StrSlice;
327     use to_str::ToStr;
328     use vec::ImmutableVector;
329
330     let base = 9600u;
331     let range = 1000;
332
333     let bases = [
334         ("32-opt", base + range * 1),
335         ("32-noopt", base + range * 2),
336         ("64-opt", base + range * 3),
337         ("64-noopt", base + range * 4),
338         ("64-opt-vg", base + range * 5),
339         ("all-opt", base + range * 6),
340         ("snap3", base + range * 7),
341         ("dist", base + range * 8)
342     ];
343
344     let path = os::getcwd().to_str();
345
346     let mut final_base = base;
347
348     for &(dir, base) in bases.iter() {
349         if path.contains(dir) {
350             final_base = base;
351             break;
352         }
353     }
354
355     return final_base;
356 }
357
358 /// Get a constant that represents the number of times to repeat
359 /// stress tests. Default 1.
360 pub fn stress_factor() -> uint {
361     use os::getenv;
362
363     match getenv("RUST_RT_STRESS") {
364         Some(val) => uint::from_str(val).unwrap(),
365         None => 1
366     }
367 }