Skip to content

Conversation

@TomGoh
Copy link

@TomGoh TomGoh commented Dec 1, 2025

Changes:

  • update src/process.rs, introduce the state machine for a process, including three states: RUNNING, STOPPED, and ZOMBIE.
  • add getters and state transition functions between these states.

Changes:
- Fix the memory ordering issue in transition_to_zombie function, change from Relaxed to Release to achieve Acquire-Release ordering with zombie state setting and getting
- Remove the state transition in exit function of a process, bring the transition to do_exit for single resposibility of reparenting children in exit
- Add some comments on memory ordering
  Changes:
  - Add WaitEventFlags bitflags to ThreadGroup
  - Add wait_events and last_stop_signal fields to ThreadGroup
  - Implement set_stop_signal, set_cont_signal on Process
  - Implement peek/consume methods for stop/continue events
  - Update ThreadGroup::default() to initialize new fields

  Rationale: Events persist until zombie reaping, ensuring proper WUNTRACED/WCONTINUED reporting
@TomGoh TomGoh marked this pull request as ready for review December 8, 2025 02:55
@guoweikang guoweikang requested a review from Copilot December 8, 2025 03:09
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a state machine for process lifecycle management, replacing the simple boolean is_zombie flag with a three-state system (RUNNING, STOPPED, ZOMBIE). The implementation adds public getters for checking process states and transition methods for moving between states, with careful attention to atomic operations and memory ordering guarantees.

Key Changes

  • Replaced AtomicBool is_zombie with AtomicU8 state to support multiple process states
  • Added three state query methods (is_running(), is_stopped(), is_zombie())
  • Implemented atomic state transition methods with explicit memory ordering documentation
  • Added bitflags dependency for ProcessState definition

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
src/process.rs Introduced ProcessState bitflags, replaced is_zombie field with state field, added state query and transition methods, updated Process constructor to initialize with RUNNING state
Cargo.toml Added bitflags dependency as workspace dependency
Comments suppressed due to low confidence (1)

src/process.rs:329

  • The exit() method no longer transitions the process to the ZOMBIE state. The old code had self.is_zombie.store(true, Ordering::Release); at line 320, which was removed but not replaced with a call to self.transition_to_zombie(). This will cause the process to remain in its current state after exit, breaking the expected behavior where exit() should mark the process as a zombie (as tested in tests/process.rs:20-21).

Add self.transition_to_zombie(); before or after the children are transferred to the reaper.

    pub fn exit(self: &Arc<Self>) {
        // TODO: child subreaper
        let reaper = INIT_PROC.get().unwrap();

        if Arc::ptr_eq(self, reaper) {
            return;
        }

        let mut children = self.children.lock(); // Acquire the lock first

        let mut reaper_children = reaper.children.lock();
        let reaper = Arc::downgrade(reaper);

        for (pid, child) in core::mem::take(&mut *children) {
            *child.parent.lock() = reaper.clone();
            reaper_children.insert(pid, child);
        }
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Changes:
- Remove PartialEq impl for ProcessState
- Update process state comparison functions to use bitflags contains function
- Update transition_to_x function logic for state safety
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/process.rs:330

  • The exit() method no longer transitions the process to the ZOMBIE state. The old code had self.is_zombie.store(true, Ordering::Release) at line 321, which was removed but not replaced with a call to self.transition_to_zombie(). This breaks the expected behavior as shown in the test at tests/process.rs:21 which expects child.is_zombie() to return true after calling exit().

Add self.transition_to_zombie(); after acquiring the children lock to maintain the correct state transition.

    pub fn exit(self: &Arc<Self>) {
        // TODO: child subreaper
        let reaper = INIT_PROC.get().unwrap();

        if Arc::ptr_eq(self, reaper) {
            return;
        }

        let mut children = self.children.lock(); // Acquire the lock first

        let mut reaper_children = reaper.children.lock();
        let reaper = Arc::downgrade(reaper);

        for (pid, child) in core::mem::take(&mut *children) {
            *child.parent.lock() = reaper.clone();
            reaper_children.insert(pid, child);
        }
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…nd session test based on new state machine. Update cargo.toml
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/process.rs:330

  • The exit() method no longer transitions the process to the ZOMBIE state, which breaks the typical process lifecycle semantics. Callers now need to explicitly call transition_to_zombie() after exit(), as seen throughout the test changes. Consider either:
  1. Having exit() automatically call transition_to_zombie() at the end, or
  2. Renaming exit() to clarify it doesn't mark the process as a zombie (e.g., reparent_children())

The current API design is error-prone since forgetting to call transition_to_zombie() after exit() would leave the process in an inconsistent state.

    /// Terminates the [`Process`].
    ///
    /// Child processes are inherited by the init process or by the nearest
    /// subreaper process.
    ///
    /// This method panics if the [`Process`] is the init process.
    pub fn exit(self: &Arc<Self>) {
        // TODO: child subreaper
        let reaper = INIT_PROC.get().unwrap();

        if Arc::ptr_eq(self, reaper) {
            return;
        }

        let mut children = self.children.lock(); // Acquire the lock first

        let mut reaper_children = reaper.children.lock();
        let reaper = Arc::downgrade(reaper);

        for (pid, child) in core::mem::take(&mut *children) {
            *child.parent.lock() = reaper.clone();
            reaper_children.insert(pid, child);
        }
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (2)

src/process.rs:312

  • The documentation comment for exit() should be updated to clarify that this method does NOT transition the process to zombie state anymore (as indicated by the removal of the "marking it as a zombie process" phrase). Consider adding: "Note: This method does not change the process state. Callers should call transition_to_zombie() to mark the process as a zombie after calling this method."
    /// Terminates the [`Process`].
    ///
    /// Child processes are inherited by the init process or by the nearest
    /// subreaper process.
    ///
    /// This method panics if the [`Process`] is the init process.

src/process.rs:330

  • The exit() method no longer transitions the process to ZOMBIE state (the previous is_zombie.store(true, Ordering::Release) call was removed). This is a breaking API change that requires callers to explicitly call transition_to_zombie() after exit(). Consider either:
  1. Keeping the automatic zombie transition in exit() for backward compatibility, or
  2. Renaming this method (e.g., to reparent_children()) to better reflect its purpose and make the breaking change more obvious, or
  3. At minimum, updating the documentation to clearly state that callers must call transition_to_zombie() after this method.
    /// Terminates the [`Process`].
    ///
    /// Child processes are inherited by the init process or by the nearest
    /// subreaper process.
    ///
    /// This method panics if the [`Process`] is the init process.
    pub fn exit(self: &Arc<Self>) {
        // TODO: child subreaper
        let reaper = INIT_PROC.get().unwrap();

        if Arc::ptr_eq(self, reaper) {
            return;
        }

        let mut children = self.children.lock(); // Acquire the lock first

        let mut reaper_children = reaper.children.lock();
        let reaper = Arc::downgrade(reaper);

        for (pid, child) in core::mem::take(&mut *children) {
            *child.parent.lock() = reaper.clone();
            reaper_children.insert(pid, child);
        }
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

ProcessState::from_bits_truncate(bits).contains(ProcessState::STOPPED)
}

/// Change the [`Process`] from `Running` to `Stopped`.
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The documentation says "Change the [Process] from Running to Stopped" but the implementation allows transitioning from any non-ZOMBIE state that has RUNNING set to STOPPED. The documentation should clarify that this is not strictly a RUNNING -> STOPPED transition but rather "sets the process to STOPPED state if it's currently RUNNING and not ZOMBIE". Consider updating to: "Transitions the [Process] to Stopped state."

Suggested change
/// Change the [`Process`] from `Running` to `Stopped`.
/// Transitions the [`Process`] to `Stopped` state.

Copilot uses AI. Check for mistakes.
);
}

/// Change the [`Process`] from `Stopped` to `Running`.
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Similar to transition_to_stopped, the documentation says "Change the [Process] from Stopped to Running" but should more accurately reflect what the method does. Consider: "Transitions the [Process] to Running state."

Suggested change
/// Change the [`Process`] from `Stopped` to `Running`.
/// Transitions the [`Process`] to `Running` state.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants