//! `oxy`: command-line interface for Oxygen. //! //! Command definitions and dispatch live in `cli`; each command group has //! its own module there. mod cli; #[cfg(feature = "alloc-stats")] #[global_allocator] static ALLOC: oxy_core::alloc_stats::Counting = oxy_core::alloc_stats::Counting; // Many small allocations from every thread (paths, entries, atoms): mimalloc // serves them from per-thread pages without the arena locks of glibc's. #[cfg(not(feature = "alloc-stats"))] #[global_allocator] static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; fn main() { // Behave like other CLI tools when output is piped into `head`: die // quietly on SIGPIPE instead of panicking on a failed write. #[cfg(unix)] unsafe { libc::signal(libc::SIGPIPE, libc::SIG_DFL); } // With the counting allocator (over glibc's): glibc gives each thread // that allocates an arena of its own and keeps what is freed there: the // first commit of the Linux tree, read and hashed across threads, held // 4.3 GB for 2.7 GB in use. Two arenas hold 3.1 GB, no slower. // MALLOC_ARENA_MAX still has the last word. #[cfg(all(target_os = "linux", target_env = "gnu", feature = "alloc-stats"))] if std::env::var_os("MALLOC_ARENA_MAX").is_none() { unsafe { libc::mallopt(libc::M_ARENA_MAX, 2); } } let command = oxy_core::timing::phase("command"); let result = cli::run(); if result.is_ok() { cli::after_command(); } drop(command); oxy_core::timing::memory_summary(); match result { Ok(code) => std::process::exit(code), Err(e) => { cli::ui::init(false); eprintln!("{} {e:#}", cli::ui::err("error:")); std::process::exit(1); } } }