use crate::{IndependentBackend, SpawnLifetime, SpawnMode, SpawnOptions};
use running_process_platform_internal::platform::independent_spawn::{
spawn, spawn_broker, spawn_inherited, IndependentChild, InheritedChild, LaunchSpec,
};
use std::{
io,
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
enum Child {
Inherited(InheritedChild),
Independent(IndependentChild),
}
pub struct SpawnHandle {
child: Child,
lifetime: SpawnLifetime,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SpawnExit {
pub code: Option<i32>,
}
impl SpawnHandle {
pub fn id(&self) -> u32 {
match &self.child {
Child::Inherited(c) => c.id(),
Child::Independent(c) => c.id(),
}
}
pub fn actual_mode(&self) -> SpawnMode {
match &self.child {
Child::Inherited(_) => SpawnMode::Inherited,
Child::Independent(_) => SpawnMode::Independent,
}
}
pub fn is_alive(&mut self) -> io::Result<bool> {
match &mut self.child {
Child::Inherited(c) => c.try_wait().map(|exit| exit.is_none()),
Child::Independent(c) => Ok(c.is_alive()),
}
}
pub fn stop(&mut self, timeout: Duration) -> io::Result<()> {
match &mut self.child {
Child::Inherited(c) => c.stop(timeout),
Child::Independent(c) => c.stop(timeout),
}
}
pub fn wait(&mut self, timeout: Duration, cancelled: &AtomicBool) -> io::Result<SpawnExit> {
match &mut self.child {
Child::Inherited(c) => c
.wait(timeout, cancelled)
.map(|code| SpawnExit { code: Some(code) }),
Child::Independent(c) => {
c.wait(timeout, cancelled)?;
Ok(SpawnExit { code: None })
}
}
}
}
impl Drop for SpawnHandle {
fn drop(&mut self) {
if self.lifetime == SpawnLifetime::KillOnDrop {
let _ = self.stop(Duration::from_secs(2));
}
}
}
pub fn spawn_with_options(
spec: &LaunchSpec,
options: &SpawnOptions,
cancelled: &AtomicBool,
) -> io::Result<SpawnHandle> {
if cancelled.load(Ordering::Acquire) {
return Err(io::Error::from(io::ErrorKind::Interrupted));
}
if options.timeout.is_zero() || options.timeout > Duration::from_secs(30) {
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}
let child = match (options.mode, &options.backend) {
(SpawnMode::Inherited, None) => Child::Inherited(spawn_inherited(
spec,
options.lifetime == SpawnLifetime::Detached,
options.timeout,
cancelled,
)?),
(SpawnMode::Inherited, Some(_)) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Inherited cannot select an independent backend",
))
}
(SpawnMode::Independent, Some(IndependentBackend::NativeScheduler { launcher })) => {
Child::Independent(spawn(spec, launcher, options.timeout, cancelled)?)
}
(SpawnMode::Independent, None) => {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"Independent requires an explicit launch authority",
))
}
(SpawnMode::Independent, Some(IndependentBackend::ExternalBroker { endpoint })) => {
Child::Independent(spawn_broker(spec, endpoint, options.timeout, cancelled)?)
}
};
Ok(SpawnHandle {
child,
lifetime: options.lifetime,
})
}