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.
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.
13 use option::{Some, None};
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;
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};
30 pub fn new_test_uv_sched() -> Scheduler {
32 let mut sched = Scheduler::new(~UvEventLoop::new(),
36 // Don't wait for the Shutdown message
37 sched.no_sleep = true;
42 pub fn run_in_newsched_task(f: ~fn()) {
44 do run_in_bare_thread {
45 run_in_newsched_task_core(f.take());
49 pub fn run_in_newsched_task_core(f: ~fn()) {
51 use rt::sched::Shutdown;
53 let mut sched = ~new_test_uv_sched();
54 let exit_handle = Cell::new(sched.make_handle());
56 let on_exit: ~fn(bool) = |exit_status| {
57 exit_handle.take().send(Shutdown);
58 rtassert!(exit_status);
60 let mut task = ~Task::new_root(&mut sched.stack_pool, None, f);
61 task.death.on_exit = Some(on_exit);
63 sched.bootstrap(task);
66 #[cfg(target_os="macos")]
67 #[allow(non_camel_case_types)]
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.
74 * This fixes issue #7772.
78 type rlim_t = libc::uint64_t;
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;
92 static CTL_KERN: libc::c_int = 1;
93 static KERN_MAXFILESPERPROC: libc::c_int = 29;
94 static RLIMIT_NOFILE: libc::c_int = 8;
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;
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);
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);
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);
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);
136 #[cfg(not(target_os="macos"))]
137 mod darwin_fd_limit {
138 pub unsafe fn raise_fd_limit() {}
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()) {
146 use from_str::FromStr;
147 use rt::sched::Shutdown;
150 // Bump the fd limit on OS X. See darwin_fd_limit for an explanation.
151 unsafe { darwin_fd_limit::raise_fd_limit() }
153 let f = Cell::new(f);
155 do run_in_bare_thread {
156 let nthreads = match os::getenv("RUST_RT_TEST_THREADS") {
157 Some(nstr) => FromStr::from_str(nstr).unwrap(),
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.
166 let sleepers = SleeperList::new();
167 let work_queue = WorkQueue::new();
169 let mut handles = ~[];
170 let mut scheds = ~[];
172 for _ in range(0u, nthreads) {
173 let loop_ = ~UvEventLoop::new();
174 let mut sched = ~Scheduler::new(loop_,
177 let handle = sched.make_handle();
179 handles.push(handle);
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);
191 rtassert!(exit_status);
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);
196 let mut threads = ~[];
197 let main_task = Cell::new(main_task);
200 let sched = scheds.pop();
201 let sched_cell = Cell::new(sched);
203 let sched = sched_cell.take();
204 sched.bootstrap(main_task.take());
207 threads.push(main_thread);
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");
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());
221 threads.push(thread);
224 // Wait for schedulers
225 for thread in threads.consume_iter() {
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));
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));
242 pub fn spawntask_random(f: ~fn()) {
243 use rand::{Rand, rng};
246 let run_now: bool = Rand::rand(&mut rng);
255 pub fn spawntask_try(f: ~fn()) -> Result<(),()> {
257 let (port, chan) = oneshot();
258 let chan = Cell::new(chan);
259 let on_exit: ~fn(bool) = |exit_status| chan.take().send(exit_status);
261 let mut new_task = Task::build_root(None, f);
262 new_task.death.on_exit = Some(on_exit);
264 Scheduler::run_task(new_task);
266 let exit_status = port.recv();
267 if exit_status { Ok(()) } else { Err(()) }
271 /// Spawn a new task in a new scheduler and return a thread handle.
272 pub fn spawntask_thread(f: ~fn()) -> Thread {
274 let f = Cell::new(f);
276 let thread = do Thread::start {
277 run_in_newsched_task_core(f.take());
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, ||{}));
292 /// Use to cleanup tasks created for testing but not "run".
293 pub fn cleanup_task(mut task: ~Task) {
294 task.destroyed = true;
297 /// Get a port number, starting at 9600, for use in tests
298 pub fn next_test_port() -> u16 {
300 return rust_dbg_next_port(base_port() as libc::uintptr_t) as u16;
303 fn rust_dbg_next_port(base: libc::uintptr_t) -> libc::uintptr_t;
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() }
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() }
318 XXX: Welcome to MegaHack City.
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.
324 fn base_port() -> uint {
328 use vec::ImmutableVector;
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)
344 let path = os::getcwd().to_str();
346 let mut final_base = base;
348 for &(dir, base) in bases.iter() {
349 if path.contains(dir) {
358 /// Get a constant that represents the number of times to repeat
359 /// stress tests. Default 1.
360 pub fn stress_factor() -> uint {
363 match getenv("RUST_RT_STRESS") {
364 Some(val) => uint::from_str(val).unwrap(),