Files
craft-anything/crates/platform/src/lib.rs
Brandon Thomas 292d5af06e Stop deadlocks as they form and watch every lock for one that never lets go (#5)
* Stop deadlocks as they form and watch every lock for one that never lets go

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Give the stuck-lock banner's test a program of its own

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 20:28:16 -04:00

62 lines
3.2 KiB
Rust

//! Native platform services for Craft Anything: owned processes, file safety and program lookup.
//!
//! Every external program Craft Anything runs goes through [`process`]: stdin is closed or fed from
//! bytes we own, every run has wall and idle deadlines, output is capped and drained on threads,
//! and the whole process group (Unix) or Job Object (Windows) is killed on timeout, cancel or drop.
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
pub mod cancel;
pub mod capture;
pub mod fs;
pub mod process;
pub mod serve;
pub mod standin_server;
pub mod sync;
pub mod which;
pub mod window;
pub use capture::{Capture, Event, EventKind, Sig, Spec as CaptureSpec};
pub use fs::{backup_once, write_atomic, write_atomic_private};
pub use process::{Cancel, Ended, Launch, Launched, LineSink, Output, Process, RunningProcess, StdinMode, open_path};
pub use which::{cli_file_name, self_cli_path, which, which_all};
/// Keep the computer from sleeping while the returned guard lives (dropping it stops the helper):
/// a clone that runs for hours must not be cut off by sleep, which would also trip every time
/// limit at once on waking. The screen may still turn off. `None` where there's no helper.
// shortcut: Windows (a PowerShell helper, since this workspace forbids unsafe FFI) and macOS
// (caffeinate) only; add `systemd-inhibit` for Linux when it's needed.
pub fn keep_awake() -> Option<RunningProcess> {
// ES_CONTINUOUS | ES_SYSTEM_REQUIRED (0x80000001, in decimal: Windows PowerShell reads the hex
// literal as a negative Int32), held by the helper's thread until it's stopped. Any failure
// ends the helper, so it never runs without doing its job.
const WINDOWS: &str = "$ErrorActionPreference = 'Stop'; $k = Add-Type -Name P -Namespace CraftAnything -PassThru -MemberDefinition '[DllImport(\"kernel32.dll\")] public static extern uint SetThreadExecutionState(uint f);'; while ($true) { if ($k::SetThreadExecutionState(2147483649) -eq 0) { exit 1 }; Start-Sleep -Seconds 30 }";
let p = if cfg!(windows) {
Process::new("powershell").args(["-NoProfile", "-NonInteractive", "-Command", WINDOWS])
} else if cfg!(target_os = "macos") {
Process::new("caffeinate").arg("-i")
} else {
return None;
};
p.timeout(std::time::Duration::from_secs(7 * 24 * 3600)).label("keep awake").spawn().ok()
}
#[cfg(all(test, windows))]
mod tests {
use std::time::Duration;
#[test]
fn keep_awake_holds_until_dropped() {
let mut guard = crate::keep_awake().expect("the helper starts");
// A script error (the Windows call failing to load) would end it within moments.
assert!(guard.wait_timeout(Duration::from_secs(4)).is_none(), "still holding the computer awake");
let pid = guard.pid();
drop(guard);
std::thread::sleep(Duration::from_millis(800));
let listed = std::process::Command::new("tasklist")
.args(["/FI", &format!("PID eq {pid}"), "/NH"])
.output()
.map(|o| String::from_utf8_lossy(&o.stdout).contains(&pid.to_string()));
assert_eq!(listed.ok(), Some(false), "dropping the guard stops the helper");
}
}