cloudfox-coreshift-core 2.33.0

Low-level Linux and Android systems primitives for CoreShift (CloudFox)
Documentation
# Signal Handling and Shutdown Coordination

CoreShift-Core provides a centralized mechanism for managing process shutdown via standard Unix signals (`SIGINT`, `SIGTERM`), plus a full `SignalRuntime` for blocking, waiting, and dispatching signals.

## Design Goal

Core does not impose a shutdown policy. Instead, it provides a thread-safe way for a signal handler to notify the application's main loop that a shutdown has been requested, and low-level primitives for masking/waiting/signalfd.

## Shutdown Primitives

### `install_shutdown_flag`

`install_shutdown_flag(flag: &'static AtomicBool)` installs global signal handlers for `SIGINT` and `SIGTERM`. When either signal is received, the handler sets the provided `AtomicBool` to `true`.

This is intended for the application's primary entry point.

### `install_shutdown_flag_guard`

`install_shutdown_flag_guard(flag: &'static AtomicBool)` is the scoped version of the above. It returns a guard that, when dropped, restores the previous signal handlers and the previous shutdown flag pointer.

This is primarily used in tests or transient worker threads to ensure isolation.

### `shutdown_requested`

`shutdown_requested(flag: &AtomicBool)` is a convenience helper to check the current state of the shutdown flag using the correct atomic ordering (`Ordering::Acquire`).

## Typical Usage Pattern

```rust
use coreshift_core::signal;
use std::sync::atomic::AtomicBool;

static SHUTDOWN: AtomicBool = AtomicBool::new(false);

fn main() -> Result<(), coreshift_core::CoreError> {
    // Install the global handler
    signal::install_shutdown_flag(&SHUTDOWN)?;

    while !signal::shutdown_requested(&SHUTDOWN) {
        // Perform work...
        // If a signal arrives, the loop will exit gracefully.
    }

    Ok(())
}
```

## `SignalRuntime`

`SignalRuntime` is the lower-level surface for explicit signal control, aimed at reactor-integrated daemons:

- **Masking**: `empty_set` / `set_with(signals)` build a `SignalSet`; `block_current_thread` / `restore_current_thread` / `set_current_thread_mask` manage the calling thread's signal mask (`pthread_sigmask`).
- **Waiting**: `wait(signals)` blocks the current thread until a signal in the set arrives and returns it; `signalfd_new(signals)` creates a `signalfd` for reactor-compatible reception — the fd is non-blocking + `CLOEXEC`, and the **caller** must block the signals on its own thread before reading from it.
- **Thread targeting**: `interrupt_thread(thread, signal)` targets a specific thread (`pthread_kill`; `ESRCH` surfaces).
- **Handlers**: `register_handler(sig, handler)` installs an arbitrary process-wide handler and **returns the previous `libc::sigaction`**; `reset_default(sig)` restores `SIG_DFL`; `reset_ignored_to_default()` scans signals 1..=64 (skipping `SIGKILL`/`SIGSTOP`) and resets any `SIG_IGN`-ed handler to default — best-effort, all errors tolerated.
- **Convenience**: `kill(pid, sig)`; `blocked(signals)` **blocks immediately** (`SIG_BLOCK`) and returns a `BlockedSignals` guard that restores the previous mask on drop; `unblock_all()`.

## Lower-level items

- Constants: `SIGINT`, `SIGTERM`, `SIGPIPE`, `SIGKILL`, `SIGUSR1`, `SIGUSR2`,
  `SIGCHLD`, `SIGHUP` (8 exported `i32` consts).
- Type aliases: `SignalSet = libc::sigset_t`, `ThreadId = libc::pthread_t`,
  `SignalfdSiginfo = libc::signalfd_siginfo`.
- `signal_ignore(sig)` (`unsafe`) — install `SIG_IGN` (BSD `libc::signal`), no
  result. Used by spawn children to neutralize inherited ignores; note
  `reset_ignored_to_default` is the reverse operation.

## Safety Invariants

- **Signal Safety**: The internal shutdown handler is async-signal-safe. It only performs a `store` operation on an atomic pointer.
- **Global State**: Core uses a single `static AtomicPtr<AtomicBool>` to track the active flag (`SHUTDOWN_FLAG_PTR`, signal.rs:46). Only one flag can be active at a time per process.
- **Race closure (CORE-M11)**: `SIGINT`/`SIGTERM` are blocked for the entire install (and the entire guard drop), so no signal can be delivered between handler swap and flag-pointer swap.
- **`unblock_all` (CORE-M12)**: sets the whole mask to empty — only safe in a single-threaded fork child.
- **Signalfd restore**: `Reactor::drop` restores the signalfd mask only if it runs on the same thread that set it up (`pthread_equal`). `BlockedSignals::drop` restores the mask unconditionally on whatever thread drops it — do not drop it from a different thread than the one that created it. Also note `blocked()` swallows `pthread_sigmask` failure and would then restore an empty mask; avoid relying on it after an error.
- **Footguns**: `set_with` reports `EINVAL` on any `sigaddset` failure; `register_handler` installs with `sa_flags = 0` (no `SA_RESTART`).