ProcessGroup

Struct ProcessGroup 

Source
pub struct ProcessGroup { /* private fields */ }
Expand description

A cross-platform wrapper for managing process groups/jobs.

On Unix systems, this uses process groups with setsid(). On Windows, this uses Job Objects for full process tree termination.

§Platform Support

  • Unix/Linux: Full process group support using setsid() and killpg()
  • Windows: Full process tree support using Job Objects
  • Other platforms: No special handling

Implementations§

Source§

impl ProcessGroup

Source

pub fn pause_group(&self) -> Result<(), ProcessGroupError>

Pauses all processes in the group (Unix).

Sends SIGSTOP to the process group using killpg().

§Returns
  • Ok(()) - If the signal was sent successfully or processes were already stopped
  • Err(ProcessGroupError) - If pausing fails due to permissions or other errors
§Example
use tcrm_task::tasks::process::group::builder::ProcessGroup;
let mut group = ProcessGroup::new();
// ... spawn processes in the group ...
group.pause_group().unwrap();
Source§

impl ProcessGroup

Source

pub fn resume_group(&self) -> Result<(), ProcessGroupError>

Resumes all processes in the group (Unix).

Sends SIGCONT to the process group using killpg().

§Returns
  • Ok(()) - If the signal was sent successfully
  • Err(ProcessGroupError) - If resuming fails due to permissions or other errors
§Example
use tcrm_task::tasks::process::group::builder::ProcessGroup;
let mut group = ProcessGroup::new();
// ... spawn and pause processes in the group ...
group.resume_group().unwrap();
Source§

impl ProcessGroup

Source

pub fn stop_group(&self) -> Result<(), ProcessGroupError>

Terminates all processes in the group (Unix).

Sends SIGTERM to the process group using killpg().

§Returns
  • Ok(()) - If the signal was sent successfully or processes were already terminated
  • Err(ProcessGroupError) - If termination fails due to permissions or other errors
§Example
use tcrm_task::tasks::process::group::builder::ProcessGroup;
let mut group = ProcessGroup::new();
// ... spawn processes in the group ...
group.stop_group().unwrap();
Source§

impl ProcessGroup

Source

pub fn new() -> Self

Create a new, inactive process group

§Returns

A new ProcessGroup instance that is not yet active

§Examples
use tcrm_task::tasks::process::group::builder::ProcessGroup;

let group = ProcessGroup::new();
assert!(!group.is_active());
Source

pub fn is_active(&self) -> bool

Check if the process group is active

§Returns

true if the process group has been created and is active, false otherwise

§Examples
use tcrm_task::tasks::process::group::builder::ProcessGroup;

let group = ProcessGroup::new();
assert!(!group.is_active());
Source

pub fn create_with_command( &mut self, command: Command, ) -> Result<Command, ProcessGroupError>

Creates a new process group and configures the command to use it.

This method prepares a Command to run as part of this process group. On Unix systems, it configures the command to create a new session and process group using setsid(). On Windows, it configures the command to run in a new job object with appropriate creation flags and enables CREATE_SUSPENDED to avoid race conditions.

§Arguments
  • command - The Command to configure for process group execution
§Returns
  • Ok(Command) - The configured command ready for execution
  • Err(ProcessGroupError) - If process group configuration fails
§Platform-Specific Behavior
§Windows Race Condition Mitigation

On Windows, the process is configured to start in a suspended state (CREATE_SUSPENDED). After spawning, you must call assign_child() and then manually resume the process to avoid the race condition where child processes can escape the job before assignment.

§Unix Behavior

On Unix, the process starts normally in its own process group via setsid().

§Example
use tcrm_task::tasks::process::group::builder::ProcessGroup;
use tokio::process::Command;

let mut group = ProcessGroup::new();
let mut cmd = Command::new("echo");
cmd.arg("hello");

let configured_cmd = group.create_with_command(cmd).unwrap();
// Command is now configured to run in the process group
Source

pub fn assign_child(&mut self, child_id: u32) -> Result<(), ProcessGroupError>

Assigns a spawned child process to this process group/job.

On Unix systems, this stores the process group ID.

On Windows, this assigns the process to the job object.

After assignment, all future children of the process will be contained in the job, unless the process has breakaway privileges (which are not enabled by default in this implementation).

§Arguments
  • child_id - The process ID of the child to assign to this group
§Returns
  • Ok(()) - If the assignment was successful
  • Err(ProcessGroupError) - If assignment fails or the platform is unsupported
§Example
use tcrm_task::tasks::process::group::builder::ProcessGroup;
use std::process::Command;

let mut group = ProcessGroup::new();

// After spawning a process, assign it to the group
// let child = Command::new("echo").spawn()?;
// group.assign_child(child.id())?;
§Windows Race Condition Note

On Windows, there is a well-known race condition: if a spawned process creates child processes before it is assigned to the job object, those children will not be part of the job and can escape containment.

See: https://devblogs.microsoft.com/oldnewthing/20130405-00/?p=4743

To avoid this issue, the process needs to be spawned in a suspended state, assigned to the job object, and only then resuming it. This ensures that no child processes can escape the job before assignment.

Source

pub fn resume_process(&self, _child_id: u32) -> Result<(), ProcessGroupError>

No-op on non-Windows platforms.

Trait Implementations§

Source§

impl Debug for ProcessGroup

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.