From 98dc7b15afb7bcbbbb47d13a40ecf6dfdad0c9f9 Mon Sep 17 00:00:00 2001
From: Graydon Hoare <graydon@mozilla.com>
Date: Fri, 9 Aug 2013 16:30:44 -0700
Subject: [PATCH] Try to fix mac valgrind bot by disabling thread-heavy
 activities.

---
 src/libstd/rt/comm.rs      | 10 ++++++++++
 src/libstd/rt/sched.rs     |  2 ++
 src/libstd/rt/test.rs      | 14 +++++++++-----
 src/libstd/rt/util.rs      | 26 +++++++++++++++++++++++++-
 src/libstd/run.rs          |  9 +--------
 src/libstd/unstable/mod.rs | 14 ++++++++++++++
 6 files changed, 61 insertions(+), 14 deletions(-)

diff --git a/src/libstd/rt/comm.rs b/src/libstd/rt/comm.rs
index 0cf223f302913..afd1192baf9dd 100644
--- a/src/libstd/rt/comm.rs
+++ b/src/libstd/rt/comm.rs
@@ -692,6 +692,7 @@ mod test {
     use rt::test::*;
     use cell::Cell;
     use iter::Times;
+    use rt::util;
 
     #[test]
     fn oneshot_single_thread_close_port_first() {
@@ -840,6 +841,7 @@ mod test {
 
     #[test]
     fn oneshot_multi_thread_close_stress() {
+        if util::limit_thread_creation_due_to_osx_and_valgrind() { return; }
         do stress_factor().times {
             do run_in_newsched_task {
                 let (port, chan) = oneshot::<int>();
@@ -855,6 +857,7 @@ mod test {
 
     #[test]
     fn oneshot_multi_thread_send_close_stress() {
+        if util::limit_thread_creation_due_to_osx_and_valgrind() { return; }
         do stress_factor().times {
             do run_in_newsched_task {
                 let (port, chan) = oneshot::<int>();
@@ -875,6 +878,7 @@ mod test {
 
     #[test]
     fn oneshot_multi_thread_recv_close_stress() {
+        if util::limit_thread_creation_due_to_osx_and_valgrind() { return; }
         do stress_factor().times {
             do run_in_newsched_task {
                 let (port, chan) = oneshot::<int>();
@@ -901,6 +905,7 @@ mod test {
 
     #[test]
     fn oneshot_multi_thread_send_recv_stress() {
+        if util::limit_thread_creation_due_to_osx_and_valgrind() { return; }
         do stress_factor().times {
             do run_in_newsched_task {
                 let (port, chan) = oneshot::<~int>();
@@ -920,6 +925,7 @@ mod test {
 
     #[test]
     fn stream_send_recv_stress() {
+        if util::limit_thread_creation_due_to_osx_and_valgrind() { return; }
         do stress_factor().times {
             do run_in_mt_newsched_task {
                 let (port, chan) = stream::<~int>();
@@ -964,6 +970,7 @@ mod test {
 
     #[test]
     fn shared_chan_stress() {
+        if util::limit_thread_creation_due_to_osx_and_valgrind() { return; }
         do run_in_mt_newsched_task {
             let (port, chan) = stream();
             let chan = SharedChan::new(chan);
@@ -983,6 +990,7 @@ mod test {
 
     #[test]
     fn shared_port_stress() {
+        if util::limit_thread_creation_due_to_osx_and_valgrind() { return; }
         do run_in_mt_newsched_task {
             // XXX: Removing these type annotations causes an ICE
             let (end_port, end_chan) = stream::<()>();
@@ -1063,6 +1071,8 @@ mod test {
         use rand;
         use rand::RngUtil;
 
+        if util::limit_thread_creation_due_to_osx_and_valgrind() { return; }
+
         do run_in_mt_newsched_task {
             let (end_port, end_chan) = stream::<()>();
             let end_chan = SharedChan::new(end_chan);
diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs
index 990e1a4a3de99..20d6e7da2f946 100644
--- a/src/libstd/rt/sched.rs
+++ b/src/libstd/rt/sched.rs
@@ -754,6 +754,7 @@ mod test {
     use cell::Cell;
     use rt::thread::Thread;
     use rt::task::{Task, Sched};
+    use rt::util;
     use option::{Some};
 
     #[test]
@@ -971,6 +972,7 @@ mod test {
 
     #[test]
     fn test_stress_schedule_task_states() {
+        if util::limit_thread_creation_due_to_osx_and_valgrind() { return; }
         let n = stress_factor() * 120;
         for _ in range(0, n as int) {
             test_schedule_home_states();
diff --git a/src/libstd/rt/test.rs b/src/libstd/rt/test.rs
index 792ea5eb33f5a..ad281408966ed 100644
--- a/src/libstd/rt/test.rs
+++ b/src/libstd/rt/test.rs
@@ -18,7 +18,7 @@ use iterator::{Iterator, range};
 use vec::{OwnedVector, MutableVector};
 use super::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
 use rt::sched::Scheduler;
-use unstable::run_in_bare_thread;
+use unstable::{run_in_bare_thread};
 use rt::thread::Thread;
 use rt::task::Task;
 use rt::uv::uvio::UvEventLoop;
@@ -156,10 +156,14 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
         let nthreads = match os::getenv("RUST_RT_TEST_THREADS") {
             Some(nstr) => FromStr::from_str(nstr).unwrap(),
             None => {
-                // Using more threads than cores in test code
-                // to force the OS to preempt them frequently.
-                // Assuming that this help stress test concurrent types.
-                util::num_cpus() * 2
+                if util::limit_thread_creation_due_to_osx_and_valgrind() {
+                    1
+                } else {
+                    // Using more threads than cores in test code
+                    // to force the OS to preempt them frequently.
+                    // Assuming that this help stress test concurrent types.
+                    util::num_cpus() * 2
+                }
             }
         };
 
diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs
index 40e5c8d4bf1a0..1886790c708d3 100644
--- a/src/libstd/rt/util.rs
+++ b/src/libstd/rt/util.rs
@@ -15,6 +15,7 @@ use libc;
 use option::{Some, None};
 use os;
 use str::StrSlice;
+use unstable::running_on_valgrind;
 
 /// Get the number of cores available
 pub fn num_cpus() -> uint {
@@ -27,12 +28,35 @@ pub fn num_cpus() -> uint {
     }
 }
 
+/// Valgrind has a fixed-sized array (size around 2000) of segment descriptors wired into it; this
+/// is a hard limit and requires rebuilding valgrind if you want to go beyond it. Normally this is
+/// not a problem, but in some tests, we produce a lot of threads casually. Making lots of threads
+/// alone might not be a problem _either_, except on OSX, the segments produced for new threads
+/// _take a while_ to get reclaimed by the OS. Combined with the fact that libuv schedulers fork off
+/// a separate thread for polling fsevents on OSX, we get a perfect storm of creating "too many
+/// mappings" for valgrind to handle when running certain stress tests in the runtime.
+#[cfg(target_os="macos")]
+pub fn limit_thread_creation_due_to_osx_and_valgrind() -> bool {
+    running_on_valgrind()
+}
+
+#[cfg(not(target_os="macos"))]
+pub fn limit_thread_creation_due_to_osx_and_valgrind() -> bool {
+    false
+}
+
 /// Get's the number of scheduler threads requested by the environment
 /// either `RUST_THREADS` or `num_cpus`.
 pub fn default_sched_threads() -> uint {
     match os::getenv("RUST_THREADS") {
         Some(nstr) => FromStr::from_str(nstr).unwrap(),
-        None => num_cpus()
+        None => {
+            if limit_thread_creation_due_to_osx_and_valgrind() {
+                1
+            } else {
+                num_cpus()
+            }
+        }
     }
 }
 
diff --git a/src/libstd/run.rs b/src/libstd/run.rs
index ef3d881c5fead..d5f57692aa6d4 100644
--- a/src/libstd/run.rs
+++ b/src/libstd/run.rs
@@ -923,6 +923,7 @@ mod tests {
     use path::Path;
     use run;
     use str;
+    use unstable::running_on_valgrind;
 
     #[test]
     #[cfg(windows)]
@@ -1331,12 +1332,4 @@ mod tests {
 
         assert!(output.contains("RUN_TEST_NEW_ENV=123"));
     }
-
-    fn running_on_valgrind() -> bool {
-        unsafe { rust_running_on_valgrind() != 0 }
-    }
-
-    extern {
-        fn rust_running_on_valgrind() -> uintptr_t;
-    }
 }
diff --git a/src/libstd/unstable/mod.rs b/src/libstd/unstable/mod.rs
index f721dd47a66a0..7a6f9bd6d2224 100644
--- a/src/libstd/unstable/mod.rs
+++ b/src/libstd/unstable/mod.rs
@@ -14,6 +14,7 @@ use comm::{GenericChan, GenericPort};
 use comm;
 use prelude::*;
 use task;
+use libc::uintptr_t;
 
 pub mod dynamic_lib;
 
@@ -116,3 +117,16 @@ pub fn change_dir_locked(p: &Path, action: &fn()) -> bool {
         fn rust_drop_change_dir_lock();
     }
 }
+
+
+/// Dynamically inquire about whether we're running under V.
+/// You should usually not use this unless your test definitely
+/// can't run correctly un-altered. Valgrind is there to help
+/// you notice weirdness in normal, un-doctored code paths!
+pub fn running_on_valgrind() -> bool {
+    unsafe { rust_running_on_valgrind() != 0 }
+}
+
+extern {
+    fn rust_running_on_valgrind() -> uintptr_t;
+}