Run cargo with a large stack
authorAlex Crichton <alex@alexcrichton.com>
Sun, 16 Aug 2015 17:47:39 +0000 (10:47 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Sun, 16 Aug 2015 17:50:55 +0000 (10:50 -0700)
There have been a number of reports of Cargo triggering a stack overflow in the
algorithm implemented in `cargo::core::resolve`, and although many attempts have
been made to reduce the stack space of the two relevant recursive functions it
seems likely that this will not always be enough. For now, before moving the
recursion to the heap manually, spawn the main thread with a large stack (e.g.
mirror what the compiler does) to ensure that the same scenarios happen across
platforms at least.

Currently on my machine I get a 2MB stack on Linux and a 512K stack on OSX, so
bumping this up to 8MB should be more than enough for the recursion in this
algorithm. I also hope that with nonzeroing drop a few of the recursive calls
will be able to become tail recursive, which should also help with stack space!

Closes #1897

src/bin/cargo.rs

index bfa763852e021c3456e26a804f766cd75035011e..3442357209f8af55aee4f26882a93de1a2ba6637 100644 (file)
@@ -11,6 +11,7 @@ use std::fs;
 use std::io;
 use std::path::{PathBuf, Path};
 use std::process::Command;
+use std::thread::Builder;
 
 use cargo::{execute_main_without_stdin, handle_error, shell};
 use cargo::core::MultiShell;
@@ -57,7 +58,18 @@ See 'cargo help <command>' for more information on a specific command.
 
 fn main() {
     env_logger::init().unwrap();
-    execute_main_without_stdin(execute, true, USAGE)
+
+    // Right now the algorithm in cargo::core::resolve is pretty recursive and
+    // runs the risk of blowing the stack. Platforms tend to have different
+    // stack limits by default (I just witnessed 512K on OSX and 2MB on Linux)
+    // so to get a consistent experience just spawn ourselves with a large stack
+    // size.
+    let stack_size = env::var("CARGO_STACK_SIZE").ok()
+                         .and_then(|s| s.parse().ok())
+                         .unwrap_or(8 * 1024 * 1024); // 8MB
+    Builder::new().stack_size(stack_size).spawn(|| {
+        execute_main_without_stdin(execute, true, USAGE)
+    }).unwrap().join().unwrap();
 }
 
 macro_rules! each_subcommand{ ($mac:ident) => ({