Run the Rust CLI on a large-stack worker thread

The clap derive-generated argument parser is deeply recursive; in debug
builds (no inlining) parsing the Command enum exhausted the default
8 MiB main-thread stack once the alarm subcommands grew it, crashing
mxgw.exe with STATUS_STACK_OVERFLOW at startup — which failed the Rust
leg of the client e2e matrix. Move parse + dispatch onto a dedicated
32 MiB worker thread so the CLI is robust regardless of build profile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-21 20:12:09 -04:00
parent 3e22285f09
commit 5e493484f1
+25 -6
View File
@@ -465,13 +465,32 @@ enum CliValueType {
String,
}
#[tokio::main]
async fn main() -> ExitCode {
/// Entry point. The real work runs on a dedicated thread with a large stack:
/// clap's derive-generated argument parser is deeply recursive, and in debug
/// builds (no inlining) parsing the `Command` enum can exhaust the default
/// 8 MiB main-thread stack as the enum grows. A 32 MiB worker stack keeps the
/// CLI robust regardless of build profile or future subcommand growth.
fn main() -> ExitCode {
let worker = std::thread::Builder::new()
.name("mxgw-cli".to_owned())
.stack_size(32 * 1024 * 1024)
.spawn(run)
.expect("failed to spawn the CLI worker thread");
worker.join().expect("the CLI worker thread panicked")
}
fn run() -> ExitCode {
let cli = Cli::parse();
let result = match cli.command {
Command::Batch => run_batch().await,
command => dispatch(command).await,
};
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("failed to build the Tokio runtime");
let result = runtime.block_on(async {
match cli.command {
Command::Batch => run_batch().await,
command => dispatch(command).await,
}
});
match result {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {