Replace the driver/session/action/event abstraction with a single OS-process supervisor thread and a minimal lifecycle-only public API.
- supervisor: add a dedicated swactor-process-supervisor thread that owns the child, runs it with null stdio, wakes via an eventfd plus poll(2), reaps with waitpid(WNOHANG), and escalates SIGTERM to SIGKILL after a deadline, reporting only lifecycle ThreadEvents over a SegQueue plus wake channel
- actor: collapse ProcessActor<D> into a non-generic state machine (Spawning/Running/Stopping/Done) that owns the supervisor handle, drains events on SupervisorWake, forwards lifecycle as ProcessOutput, and triggers shutdown_now in on_stop
- lifecycle: add ProcessOutputConfig (Disabled/DatastreamMirror) with a JSON proc.<label>.lifecycle mirror (schema swactor_process.lifecycle.v1), command-basename label derivation/sanitization, and an RAII reservation registry preventing duplicate channels
- message/types/spawn/lib: trim the API — ProcessCommand is now only Stop { kill_after }, ProcessOutput covers Started/SpawnFailed/Exited/Error, ProcessSpec keeps command/args/env/working_dir/label; re-export spawn_local_process/send_process_command and drop the custom-driver spawn_process
- removed: delete the action/event/local/mock/session modules and the ProcessDriver/ProcessWaker/EventQueue/PtySize/ProcessMode types plus the old test suite (actor_scenarios, e2e_process, local_driver, proptest_session, session_scenarios); add public_api_stage1/2 tests and the SWACTOR_MANAGED_PROCESS_SPEC.md
- swactor core: demote ProcessOutputObserver to a legacy/custom adapter (no longer auto-attached), remove Runtime::set_process_output_observer and Ctx::process_output_observer, and add the datastream dependency to the process crate for the mirror
Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
36 lines
1.1 KiB
Rust
36 lines
1.1 KiB
Rust
use std::sync::{Arc, OnceLock};
|
|
|
|
use swactor::Error;
|
|
use swactor::actor::{ActorAddress, Ctx};
|
|
use swactor::runtime::ExternalSender;
|
|
|
|
use crate::actor::ProcessActor;
|
|
use crate::lifecycle::{ProcessOutputConfig, prepare_process_output};
|
|
use crate::message::{ProcessActorCommand, ProcessCommand};
|
|
use crate::types::ProcessSpec;
|
|
|
|
/// Spawn a process actor using the OS subprocess supervisor.
|
|
pub fn spawn_local_process(
|
|
ctx: &Ctx,
|
|
sender: &ExternalSender,
|
|
spec: ProcessSpec,
|
|
output: ProcessOutputConfig,
|
|
) -> Result<ActorAddress, Error> {
|
|
let prepared_output = prepare_process_output(&spec, output)?;
|
|
let addr_slot = Arc::new(OnceLock::new());
|
|
let actor = ProcessActor::new(spec, prepared_output, sender.clone(), addr_slot.clone());
|
|
let addr = ctx.spawn(actor)?;
|
|
addr_slot
|
|
.set(addr)
|
|
.expect("process actor address already set");
|
|
let _ = sender.send_to(addr, ProcessActorCommand::SupervisorWake);
|
|
Ok(addr)
|
|
}
|
|
|
|
pub fn send_process_command(
|
|
sender: &ExternalSender,
|
|
process: ActorAddress,
|
|
command: ProcessCommand,
|
|
) -> Result<(), Error> {
|
|
sender.send_to(process, ProcessActorCommand::Command(command))
|
|
}
|