coreshift-core 1.2.6

Low-level Linux and Android systems primitives for CoreShift
Documentation
# Reactor and I/O Subsystem

CoreShift-Core implements a high-performance, non-blocking I/O system based on the Linux `epoll` facility.

## Design Philosophy

The I/O subsystem is designed to be **explicit** and **stateless**. It does not include a background thread pool or a complex async runtime. Instead, it provides the building blocks for callers to drive their own event loops.

## Components

### `Reactor`

The `Reactor` is a thin wrapper around a Linux `epoll` file descriptor.

- **Edge-Triggered**: Core uses `EPOLLET` (edge-triggered) readiness. This means that when a readiness event occurs, the caller **must** continue reading or writing to the descriptor until it receives an `EAGAIN` or `EWOULDBLOCK` error.
- **Tokens**: Every registered descriptor is associated with a `Token` (an opaque `u64`). This allows the caller to quickly identify which component is ready for I/O.

### `Fd`

`Fd` is an owned file descriptor wrapper. It ensures that descriptors are closed correctly on drop and provides safe, non-blocking helper methods for `read` and `write`.

### `DrainState`

`DrainState` is a high-level orchestrator for process I/O. It manages the bookkeeping for a process's `stdin`, `stdout`, and `stderr` pipes.

- **Multiplexing**: It coordinates reading from multiple output streams into internal buffers while respecting a global memory limit.
- **Early Exit**: Supports a predicate that can stop consumption early (e.g., if a specific log line is detected).
- **Stateless**: `DrainState` does not own the `Reactor`. It provides tokens and handlers that the caller must integrate into their own event loop.

## Typical usage with Process Spawning

When using `spawn_start`, you receive a `RunningProcess` handle which contains a `DrainState`.

```rust
use coreshift_core::spawn::{self, SpawnBackend, SpawnOptions};
use coreshift_core::reactor::Reactor;

fn example() -> Result<(), coreshift_core::CoreError> {
    let mut reactor = Reactor::new()?;
    let mut running = spawn::spawn_start(
        SpawnOptions::builder(vec!["/bin/ls".to_string()], SpawnBackend::PosixSpawn)
            .capture_stdout()
            .build()?
    )?;

    // Register process pipes with the reactor
    running.register_with_reactor(&mut reactor)?;

    let mut events = Vec::new();
    while !running.io_done() {
        reactor.wait(&mut events, 64, -1)?;
        for ev in &events {
            // Let the process handle its own readiness events
            running.handle_reactor_event(&mut reactor, ev)?;
        }
    }

    let (stdout, stderr) = running.into_output_parts();
    Ok(())
}
```

## Guarantees

- **No Hidden Threads**: All I/O occurs on the thread that calls `Reactor::wait` and the subsequent handler methods.
- **Memory Safety**: `DrainState` enforces strict bounds on captured output to prevent process-driven memory exhaustion.