# Quickstart

> ForkUnion for Rust

__`ForkUnion`__ is dead-simple to use!
There is no nested parallelism, exception handling, or "future promises"; they are banned.
A pool runs one dispatch at a time and refuses any other with the `contended` status, including one from inside its own callbacks.
The thread pool itself has a few core operations:

- `spawn` to initialize worker threads, and
- `for_threads` to launch a blocking callback on all threads.

Higher-level APIs for index-addressable tasks are also available:

- `for_n` - for individual evenly-sized tasks,
- `for_n_dynamic` - for individual unevenly-sized tasks,
- `for_slices` - for contiguous runs of evenly-sized tasks, one per worker.

For additional flow control and tuning, following helpers are available:

- `nap` - to park workers on the CPU's address monitor, waking with no syscall,
- `sleep(microseconds)` - to block workers in the kernel for long idle periods, with 0 waiting for the next dispatch,
- `terminate` - to kill the threads before the destructor is called,
- `unsafe_for_threads` - to broadcast a callback without blocking, returning a generation token,
- `unsafe_join` - to block until the completion of a broadcasted generation,
- `is_complete` - to poll a generation token for completion without blocking.

Every dispatch is identified by a `generation` token.
On `caller_exclusive_k` pools you can dispatch, overlap your own work, poll `is_complete`, and join - the classic poll-then-join pattern.
On `caller_inclusive_k` pools the calling thread owes one slice of the work, which only runs inside `unsafe_join`, so completion can't be reached by polling alone.
The same rule shapes the RAII guard returned by `for_threads` and, in C++, the `for_n` family: the workers start at the guard's construction on every pool, and on inclusive pools the caller's own slice runs at `join` or destruction.

On `caller_exclusive_k` pools thread 0 may also fork from inside its own slice, at any depth: such a fork waits for every other slice of the one enclosing it, runs on all threads, and returns complete.
So `unsafe_for_threads` can hand a whole multi-step call to thread 0, which forks the rest of the pool as it goes, while the caller only polls a status word the call stores at its end.
That is how one manager thread keeps several exclusive pools busy with no queue and no extra thread.
A join on such a pool waits for whatever dispatch runs, since the call may end before its submitter stores the token.

In C++ it provides [NUMA extensions](https://www.unum.cloud/docs/forkunion/rust/numa.md).
That includes the `colocated_pool` analog of the `flat_pool` and the `domain_allocator_t` for allocating memory in a specific memory domain.
Those are out-of-the-box compatible with the higher-level APIs.
Most interestingly, for Big Data applications, a higher-level `distributed_pool` class addresses and balances the work across all compute domains.

> `domain_allocator_t` is backed by `linux_numa_allocator_t`, `freebsd_numa_allocator_t`, or `windows_numa_allocator_t`.

To integrate into your Rust project, add the following lines to Cargo.toml:

```toml
[dependencies]
forkunion = "4.0.0-rc.1"                                          # detect what the platform offers
forkunion = { version = "4.0.0-rc.1", features = ["portable"] }   # STL thread pool only
forkunion = { version = "4.0.0-rc.1", features = ["place-memory-on-domain"] } # require NUMA-aware allocations
```

Or for the preview development version:

```toml
[dependencies]
forkunion = { git = "https://github.com/ashvardanian/ForkUnion.git", branch = "main-dev" }
```

A minimal example may look like this:

```rust
use forkunion as fu;
let topology = fu::Topology::new().expect("Failed to detect hardware topology");
let mut pool = fu::spawn(&topology, 2);
pool.broadcast(|thread_index, compute_domain_index| {
    println!("Hello from thread # {} on compute domain # {}", thread_index + 1, compute_domain_index + 1);
});
```

Higher-level APIs distribute index-addressable tasks across the threads in the pool:

```rust
pool.for_n(100, |task, at| {
    println!("Running task {} on thread # {}", task + 1, at.thread + 1);
})?;
pool.for_slices(100, |range, at| {
    println!("Running run [{}, {}) on thread # {}", range.first, range.end(), at.thread + 1);
})?;
pool.for_n_dynamic(100, |task, at| {
    println!("Running task {} on thread # {}", task + 1, at.thread + 1);
})?;
```

To let a worker closure borrow the caller's stack, reach for a scope.
`Scope` dispatches and joins on the calling thread; `ScopeView` is its `Copy` read-only half, and is what a worker carries:

```rust
let counter = fu::SpinMutex::new(0usize);
pool.scope(|scope| {
    let view = scope.view();
    scope.broadcast(|thread_index, compute_domain_index| {
        let local = view.locate_thread_in(thread_index, compute_domain_index);
        println!("thread # {} is # {} within its compute domain", thread_index, local);
        *counter.lock() += 1;
    });
});
```

`Scope` is deliberately not `Sync`, so a worker cannot capture it and start a second dispatch on a pool that is still mid-generation — the pool would refuse it as contended.
Taking a `ScopeView` first is the one line that separates the two capabilities.

A more realistic example with named threads and error handling may look like this:

```rust
use std::error::Error;
use forkunion as fu;

fn heavy_math(_: usize) {}

fn main() -> Result<(), Box<dyn Error>> {
    let topology = fu::Topology::new()?;
    let mut pool = fu::ThreadPool::named_spawn(&topology, "heavy-math", 4)?;
    pool.for_n_dynamic(400, |task, _at| {
        heavy_math(task);
    })?;
    Ok(())
}
```

The `Topology` handle reports the [hardware topology](https://www.unum.cloud/docs/forkunion/rust/topology.md), to size and place work:

```rust
let topology = fu::Topology::new()?;
for domain in 0..topology.compute_domains_count()? {
    let domain = fu::ComputeDomain(domain);
    println!("domain {}: {} cores, level {}, allocate on memory domain {}",
        domain.get(), topology.logical_cores_count_in(domain)?,
        topology.compute_level_in(domain)?.get(), topology.local_memory_of(domain)?.get());
}
```

For advanced usage, refer to the [NUMA section below](https://www.unum.cloud/docs/forkunion/rust/numa.md).
For Rayon-style parallel iterators, import the `prelude` module and [check out related examples](https://www.unum.cloud/docs/forkunion/rust/parallel-iterators.md).

`use forkunion::ThreadPool;`

`use forkunion::BroadcastJoin;`

`use forkunion::Scope;`

`use forkunion::ScopeView;`

`use forkunion::ForDispatch;`

`use forkunion::StaticIndices;`

`use forkunion::DynamicIndices;`

`use forkunion::StaticSlices;`

`use forkunion::Capabilities;`

`use forkunion::CallerExclusivity;`

`use forkunion::Status;`

`use forkunion::Error;`

`use forkunion::TasksRange;`

`use forkunion::SyncConstPtr;`

`use forkunion::SyncMutPtr;`

`use forkunion::IndexedSplit;`

`use forkunion::*;`

## Result

The result of every fallible call in this crate.

```rust
pub type Result<T> = Result<T, Error>;
```

## ThreadPool

Minimalistic, fixed-size thread-pool for blocking scoped parallelism.

```rust
pub struct ThreadPool
```

This is a safe Rust wrapper around the precompiled C thread pool implementation.
The current thread __participates__ in the work, so for `N`-way parallelism the implementation
actually spawns __N − 1__ background workers and runs the last slice on the caller thread.

#### Thread Safety

[`ThreadPool`](#ThreadPool) is `Send + Sync` and can be safely shared between threads, though operations
require a mutable reference to ensure exclusive access during execution.

#### Performance Characteristics

- Zero dynamic allocations during task execution

- Leverages weak memory model for optimal ARM and PowerPC performance

- NUMA-aware thread placement when available

- Uses CPU-specific busy-waiting instructions for minimal latency

#### Generation Tokens

Every dispatch is identified by a `usize` generation token. The safe [`for_threads`](#for_threads) API wraps it
inside a [`BroadcastJoin`](#BroadcastJoin) guard it lends to the caller: the workers start at construction,
[`join`](#join)/`Drop` waits for them, and on [`Inclusive`](#Inclusive) pools first runs the calling thread's own
slice, so only [`Exclusive`](#Exclusive) pools can be polled with [`is_complete`](#is_complete) before it. Raw token-level
access is available through the [`unsafe_for_threads`](#unsafe_for_threads)/[`unsafe_join`](#unsafe_join) pair.

Examples: Basic usage with simple computations:

```
use forkunion::*;

// Create a thread pool with 4 threads
let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 4);

// Execute work on each thread
pool.broadcast(|thread_index, compute_domain_index| {
    println!("Thread {} on compute_domain {}", thread_index, compute_domain_index);
});

// Distribute 1000 tasks across threads
pool.for_n(1000, |task, _at| {
    // Each task gets a unique index via task
    let result = task * task;
    std::hint::black_box(result); // Prevent optimization
});
```

See also helper functions like [`for_each_task_mut`](#for_each_task_mut) for data processing.

Implements: `Send`, `Sync`, `Drop`

## spawn_with_exclusivity

Spawns a pool of the given size with the caller's participation set by `exclusivity`.

```rust
pub fn spawn_with_exclusivity(
    topology: &Topology,
    threads: usize,
    exclusivity: CallerExclusivity,
) -> Result<Self>
```

## named_spawn_with_exclusivity

Spawns a named pool of the given size with the caller's participation set by `exclusivity`.

```rust
pub fn named_spawn_with_exclusivity(
    topology: &Topology,
    name: Option<&str>,
    threads: usize,
    exclusivity: CallerExclusivity,
) -> Result<Self>
```

## named_spawn_with_capabilities

As [`named_spawn_with_exclusivity`](#named_spawn_with_exclusivity), but constrains the pool to `allowed`: clear a waiter bit to force a lower-priority busy-wait, or clear [`Capabilities::PLACE_MEMORY_ON_DOMAIN`](#PLACE_MEMORY_ON_DOMAIN) to force the flat pool, which is not NUMA-aware.

```rust
pub fn named_spawn_with_capabilities(
    topology: &Topology,
    name: Option<&str>,
    threads: usize,
    exclusivity: CallerExclusivity,
    allowed: Capabilities,
) -> Result<Self>
```

## spawn_on

Spawns a pool pinned to a single compute domain, a same-QoS core cluster.

```rust
pub fn spawn_on(
    topology: &Topology,
    compute_domain_index: usize,
    threads: usize,
    exclusivity: CallerExclusivity,
) -> Result<Self>
```

The pool's threads and NUMA-local allocations stay on `compute_domain_index`, in
`0..compute_domains_count()`. Spawn one such pool per compute domain and coordinate them
from a single thread with the generation-token API ([`for_threads`](#for_threads) guards or the raw
[`unsafe_for_threads`](#unsafe_for_threads)/[`is_complete`](#is_complete)/[`unsafe_join`](#unsafe_join)). On builds without NUMA, only compute
domain 0 is valid.

Examples: ```
use forkunion::*;
// One pool per compute domain, sized to that domain's core count.
let topology = Topology::new().unwrap();
let pools: Vec&#x3C;ThreadPool> = (0..topology.compute_domains_count().unwrap())
    .map(|c| ThreadPool::spawn_on(&#x26;topology, c, topology.logical_cores_count_in(ComputeDomain(c)).unwrap_or(1), CallerExclusivity::Exclusive).unwrap())
    .collect();
assert_eq!(pools.len(), topology.compute_domains_count().unwrap());
```

## spawn_on_with_capabilities

As [`spawn_on`](#spawn_on), but constrains the colocated pool to `allowed`.

```rust
pub fn spawn_on_with_capabilities(
    topology: &Topology,
    compute_domain_index: usize,
    threads: usize,
    exclusivity: CallerExclusivity,
    allowed: Capabilities,
) -> Result<Self>
```

## spawn

Creates a new thread pool with the specified number of threads.

```rust
pub fn spawn(topology: &Topology, threads: usize) -> Result<Self>
```

By default, uses [`CallerExclusivity::Inclusive`](#Inclusive), meaning the calling thread participates in
work execution. For `N` threads, this uses the caller thread plus `N-1` background workers.

Arguments: - `threads` - Total number of threads including the caller thread

Examples: ```
use forkunion::*;

// Create a pool that uses 4 threads total (3 spawned + caller)
let topology = Topology::new().unwrap();
let pool = ThreadPool::spawn(&#x26;topology, 4).expect("Failed to create thread pool");
assert_eq!(pool.threads_count(), 4);
```

## named_spawn

Creates a new named thread pool with the specified number of threads.

```rust
pub fn named_spawn(topology: &Topology, name: &str, threads: usize) -> Result<Self>
```

The thread pool name can be useful for debugging, profiling, and system monitoring.
On supported platforms, the name may be visible in system tools and thread listings.
Names are truncated to 15 characters, plus a null terminator, to fit platform limits.

Arguments: - `name` - Name for the thread pool, up to 15 characters

- `threads` - Total number of threads including the caller thread

Examples: ```
use forkunion::*;

let topology = Topology::new().unwrap();
let pool = ThreadPool::named_spawn(&#x26;topology, "worker_pool", 4).expect("Failed to create thread pool");
assert_eq!(pool.threads_count(), 4);
```

## caller_exclusivity

Returns whether the calling thread participates in the workload.

```rust
pub fn caller_exclusivity(&self) -> Result<CallerExclusivity>
```

Queries the pool directly rather than caching, so it stays correct across `terminate` and
re-spawning with a different exclusivity.

## capabilities

Returns the capabilities this pool was spawned with.

```rust
pub fn capabilities(&self) -> Result<Capabilities>
```

Queries the pool directly rather than caching, so it reflects the allow-mask the pool
actually honors - the intersection of the requested mask and what the machine offers.

## compute_domains_count

Returns the number of thread compute_domains in the pool.

```rust
pub fn compute_domains_count(&self) -> usize
```

Compute domains group threads sharing a memory domain, QoS level, and cache hierarchy.
This information is useful for NUMA-aware load balancing and memory allocation.

## threads_count_in

Returns the number of threads in a specific compute_domain.

```rust
pub fn threads_count_in(&self, compute_domain_index: usize) -> usize
```

This method is useful for NUMA-aware load balancing, allowing you to understand how many
threads are available in each compute_domain group.

Arguments: - `compute_domain_index` - The compute_domain to query (0-based)

Examples: ```
use forkunion::*;

let topology = Topology::new().unwrap();
let pool = spawn(&#x26;topology, 8);
let total_compute_domains = pool.compute_domains_count();

for compute_domain_index in 0..total_compute_domains {
    let thread_count = pool.threads_count_in(compute_domain_index);
    println!("ComputeDomain {} has {} threads", compute_domain_index, thread_count);
}
```

## threads_count

Returns the number of threads in the pool.

```rust
pub fn threads_count(&self) -> usize
```

## locate_thread_in

Converts a global thread index to a local thread index within a compute_domain.

```rust
pub fn locate_thread_in(&self, global_thread_index: usize, compute_domain_index: usize) -> usize
```

This is useful for distributed thread pools where threads are grouped into compute domains,
same-QoS core clusters. The local index can be used for per-compute_domain data structures
or algorithms.

#### Returns

The local thread index within the specified compute_domain.

Arguments: - `global_thread_index` - The global thread index to convert

- `compute_domain_index` - The compute_domain to get the local index for

## sleep

Transitions worker threads to a power-saving sleep state.

```rust
pub fn sleep(&mut self, micros: usize)
```

This function places worker threads into a low-power sleep state when no work is available
for extended periods, checking periodically at the specified interval.

Arguments: - `micros` - Wake-up check interval in microseconds, must be > 0

Safety: This function is __not thread-safe__ and should only be called between task batches when no
parallel operations are in progress.

Examples: ```
use forkunion::*;

let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 4);

// Process a batch of work
pool.for_n(1000, |task, _at| {
    // Do some work...
    std::hint::black_box(task * 2);
});

// Put threads to sleep between batches to save power, until the next dispatch
pool.sleep(0);

// Process another batch
pool.for_n(500, |task, _at| {
    std::hint::black_box(task * 3);
});
```

## nap

Parks worker threads on the CPU's address monitor until the next dispatch.

```rust
pub fn nap(&mut self)
```

Unlike [`sleep`](#sleep), the workers stay on their cores and wake with no syscall.

## for_threads

Executes a function on each thread of the pool while `caller` overlaps its own work.

```rust
pub fn for_threads<F, R>(&mut self, function: F, caller: impl FnOnce(&BroadcastJoin<'_>) -> R) -> R
where
    F: Fn(usize, usize) + Sync,
```

The workers start right away and `caller` receives a [`BroadcastJoin`](#BroadcastJoin) to poll or join. The
join also runs when `caller` returns or unwinds, on [`CallerExclusivity::Inclusive`](#Inclusive) pools
first running the calling thread's own slice, so `function` outlives every worker.

Arguments: - `function` - Closure executed on each thread, receiving
`(thread_index, compute_domain_index)`.

- `caller` - Closure the calling thread runs with the dispatch underway; its result is
returned.

Examples: ```
use forkunion::*;

let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 4);
pool.for_threads(
    |thread_index, compute_domain_index| {
        println!("Thread {} on compute_domain {}", thread_index, compute_domain_index);
    },
    |operation| operation.join(),
);
```

The guard is only lent, so it cannot be leaked past the dispatch:

```
use forkunion::*;
let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 2);
let escaped = pool.for_threads(|_, _| {}, |operation| operation);
```

## broadcast

Runs `function` on every thread and blocks until all of them finish.

```rust
pub fn broadcast<F>(&mut self, function: F)
where
    F: Fn(usize, usize) + Sync,
```

The ergonomic common case: unlike [`for_threads`](#for_threads), this takes the closure
__by value__ and joins internally, so there is no `&` binding or guard to manage. Reach for
[`for_threads`](#for_threads) only when you want to overlap the caller's own work with the pool and poll
[`BroadcastJoin::is_complete`](#is_complete) before joining.

Examples: ```
use forkunion::*;
let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 4);
let counter = std::sync::atomic::AtomicUsize::new(0);
pool.broadcast(|_thread_index, _compute_domain_index| {
    counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
});
assert_eq!(counter.load(std::sync::atomic::Ordering::Relaxed), pool.threads_count());
```

## scope

Runs `body` with a [`Scope`](#Scope) that can broadcast work borrowing local data and answer read-only topology queries, joining every dispatch before returning.

```rust
pub fn scope<F, R>(&mut self, body: F) -> R
where
    F: FnOnce(&Scope<'_>) -> R,
```

The scope holds the pool by shared reference, so a worker closure can query it _and_ borrow
the same stack values the caller owns - the borrow conflict that otherwise forces a
raw-pointer smuggle. Because each [`Scope::broadcast`](#broadcast) blocks until it joins, those borrows
can never outlive the work.

Take a [`ScopeView`](#ScopeView) before broadcasting to carry the queries into the workers; [`Scope`](#Scope)
itself is not `Sync`, so a nested dispatch cannot re-enter the pool mid-generation.

Examples: ```
use forkunion::*;
let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 4);
let counter = SpinMutex::new(0usize);
pool.scope(|scope| {
    let view = scope.view();
    scope.broadcast(|thread_index, compute_domain_index| {
        let _local = view.locate_thread_in(thread_index, compute_domain_index);
        *counter.lock() += 1;
    });
});
assert_eq!(*counter.lock(), pool.threads_count());
```

## for_slices

Distributes `n` similar duration calls between threads in slices.

```rust
pub fn for_slices<F>(&mut self, n: usize, function: F) -> Result<()>
where
    F: Fn(TasksRange, ThreadInDomain) + Sync,
```

Instead of individual task assignment, this method groups tasks into contiguous slices and
assigns each slice to a thread. This reduces per-task overhead and improves cache locality.

Arguments: - `n` - Total number of tasks to distribute

- `function` - Closure executed for each run, given its [`ThreadInDomain`](https://www.unum.cloud/docs/forkunion/rust/topology.md#ThreadInDomain) and a [`TasksRange`](#TasksRange)

Examples: ```
use forkunion::*;

let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 4);

pool.for_slices(1000, |range, at| {
    // Each thread gets one contiguous run, which iterates directly
    for task in range {
        std::hint::black_box(task * task);
    }

    println!("Thread {} processed run [{}, {})", at.thread, range.first, range.end());
});
```

## for_slices_mut

Splits `data` into one contiguous chunk per thread and runs `function` on each in parallel, blocking until all threads finish.

```rust
pub fn for_slices_mut<T, F>(&mut self, data: &mut [T], function: F) -> Result<()>
where
    T: Send,
    F: Fn(&mut [T], ThreadInDomain) + Sync,
```

Each thread receives an __exclusive__ `&mut` sub-slice, so no interior mutability, `Mutex`,
or raw pointers are needed at the call site: the chunks partition `data` and therefore never
alias, and the synchronous join keeps every borrow inside `data`'s lifetime. This is the
safe replacement for a hand-rolled raw-pointer scatter.

Built on [`for_threads`](#for_threads), so `function` runs once per thread even when
its chunk is empty - unlike [`for_slices`](#for_slices), which skips an empty range.

Examples: ```
use forkunion::*;
let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 4);
let mut data = vec![0u64; 1000];
pool.for_slices_mut(&#x26;mut data, |chunk, _at| {
    for value in chunk {
        *value += 1;
    }
});
assert!(data.iter().all(|&#x26;value| value == 1));
```

## for_n

Distributes `n` similar duration calls between threads by individual indices.

```rust
pub fn for_n<F>(&mut self, n: usize, function: F) -> Result<()>
where
    F: Fn(usize, ThreadInDomain) + Sync,
```

Uses static load balancing where each thread gets a predetermined set of tasks.
This is optimal when all tasks have similar execution time.

Arguments: - `n` - Total number of tasks to distribute

- `function` - Closure executed for each task, given the task index and its [`ThreadInDomain`](https://www.unum.cloud/docs/forkunion/rust/topology.md#ThreadInDomain)

Examples: ```
use forkunion::*;

let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 4);

pool.for_n(1000, |task, _at| {
    // Simulate computation based on task index
    let result = task * task;
    std::hint::black_box(result); // Prevent optimization
});
```

## for_n_dynamic

Executes `n` uneven tasks on all threads, greedily stealing work.

```rust
pub fn for_n_dynamic<F>(&mut self, n: usize, function: F) -> Result<()>
where
    F: Fn(usize, ThreadInDomain) + Sync,
```

Uses dynamic load balancing with work-stealing. Threads that finish their assigned tasks
early will steal work from busy threads. This is optimal when task execution times vary
significantly.

Arguments: - `n` - Total number of tasks to distribute

- `function` - Closure executed for each task, given the task index and its [`ThreadInDomain`](https://www.unum.cloud/docs/forkunion/rust/topology.md#ThreadInDomain)

Examples: ```
use forkunion::*;

let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 4);

pool.for_n_dynamic(100, |task, _at| {
    // Simulate variable work duration - some tasks take longer
    let iterations = if task % 10 == 0 { 10000 } else { 100 };
    for i in 0..iterations {
        std::hint::black_box(task * i);
    }
});
```

## unsafe_for_threads

Dispatches `callback` on every thread without blocking, returning the generation token.

```rust
pub unsafe fn unsafe_for_threads(
    &self,
    callback: extern "C" fn(*mut c_void, usize, usize),
    context: *mut c_void,
) -> Result<usize>
```

This is the raw C-ABI mirror for building custom orchestration; prefer the safe
[`BroadcastJoin`](#BroadcastJoin) guard [`for_threads`](#for_threads) lends.

Safety: - Only one thread may operate the pool at a time, and only one dispatch may be in
flight: [`unsafe_join`](#unsafe_join) must complete before the next dispatch or pool destruction.

- `callback` and `context` must remain valid until [`unsafe_join`](#unsafe_join) returns.

## is_complete

Returns true if the given generation has completed on all threads.

```rust
pub fn is_complete(&self, generation: usize) -> Result<bool>
```

A `true` result also guarantees visibility of every contributor's writes. On [`Inclusive`](#Inclusive)
pools this can only turn `true` once [`unsafe_join`](#unsafe_join) contributes the calling thread's slice,
so the poll-then-join pattern is reserved for [`Exclusive`](#Exclusive) pools.

## unsafe_join

Blocks until the given generation completes; idempotent for joined generations.

```rust
pub unsafe fn unsafe_join(&self, generation: usize)
```

On [`Inclusive`](#Inclusive) pools this also executes the calling thread's slice of the work.

Safety: Must be called on the thread operating the pool; the dispatched callback and context must
still be valid.

## BroadcastJoin

A synchronization guard that waits for all threads to finish the broadcasted closure.

```rust
pub struct BroadcastJoin<'pool>
```

The closure is dispatched at __construction__, so the workers start immediately and the caller
can overlap its own work. [`join`](#join), or `Drop`, waits for completion, and on
[`CallerExclusivity::Inclusive`](#Inclusive) pools first runs the calling thread's own slice, so only
[`CallerExclusivity::Exclusive`](#Exclusive) pools may poll [`is_complete`](#is_complete) before it.

Only ever lent to the closure given to [`ThreadPool::for_threads`](#for_threads), so it cannot be leaked past
the dispatch: a `mem::forget` of an owned guard would let workers outlive what they call.

Implements: `Drop`

## generation

The generation token of this broadcast.

```rust
pub fn generation(&self) -> usize
```

## is_complete

True once the generation has fully completed on all threads.

```rust
pub fn is_complete(&self) -> bool
```

A `true` result also guarantees visibility of every contributor's writes. On [`Inclusive`](#Inclusive)
pools this can only turn `true` once [`join`](#join) contributes the calling thread's slice, so the
poll-then-join pattern is reserved for [`Exclusive`](#Exclusive) pools.

## join

Waits for every thread, running the caller's own slice first on [`Inclusive`](#Inclusive) pools.

```rust
pub fn join(&self)
```

Idempotent: the core returns at once for a generation already joined.

## Scope

A borrow-scoped handle to a thread pool, yielded by [`ThreadPool::scope`](#scope).

```rust
pub struct Scope<'pool>
```

Holding the pool by shared reference is what lets a worker closure borrow the caller's stack
data - the borrow conflict that otherwise forces a raw-pointer smuggle. Every
[`Scope::broadcast`](#broadcast) joins before returning, so those borrows are always valid.

Not [`Sync`]: the queries travel into workers as a [`ScopeView`](#ScopeView), while dispatch stays on the
calling thread. A worker that captured the scope could re-enter the pool mid-generation:

```
use forkunion::*;
let topology = Topology::new().unwrap();
let mut pool = spawn(&#x26;topology, 2);
pool.scope(|scope| {
    scope.broadcast(|_thread_index, _compute_domain_index| {
        scope.broadcast(|_, _| {});
    });
});
```

## view

The query-only half, which a worker closure may capture.

```rust
pub fn view(&self) -> ScopeView<'pool>
```

## threads_*_count

Total number of worker threads in the pool.

```rust
pub fn threads__count(&self) -> usize
```

## compute_domains_*_domains_count

Number of compute domains the pool spans.

```rust
pub fn compute_domains__domains_count(&self) -> usize
```

## threads_count_*_count_in

Number of threads pinned to the given compute domain.

```rust
pub fn threads_count__count_in(&self, compute_domain_index: usize) -> usize
```

## locate_thread_*_thread_in

Local index of a global thread within its compute domain.

```rust
pub fn locate_thread__thread_in(&self, global_thread_index: usize, compute_domain_index: usize) -> usize
```

## broadcast

Broadcasts `function` to every thread and blocks until all of them finish.

```rust
pub fn broadcast<F>(&self, function: F)
where
    F: Fn(usize, usize) + Sync,
```

The closure is borrowed for the dispatch and joined before this returns, so it may freely
borrow the stack data enclosing the [`ThreadPool::scope`](#scope) call.

## ScopeView

The read-only half of a [`Scope`](#Scope), `Copy` and `Sync`, so worker closures can carry it.

```rust
pub struct ScopeView<'pool>
```

[`Scope`](#Scope) itself is deliberately not `Sync`: dispatching from inside a dispatch re-enters the
pool mid-generation. Take a view with [`Scope::view`](#view) before broadcasting, and query through it.

Implements: `Clone`, `Copy`

## ForDispatch

How a staged dispatch hands itself to the pool when its guard drops.

```rust
pub trait ForDispatch<F>
```

Each marker fixes one C entry point and, through the bound on its impl, the callback arity that
entry point calls back with.

Implementors: - `impl<F> [ForDispatch](#ForDispatch)<F> for [StaticIndices](#StaticIndices) where F: Fn(usize, [ThreadInDomain](https://www.unum.cloud/docs/forkunion/rust/topology.md#ThreadInDomain)) + Sync`
- `impl<F> [ForDispatch](#ForDispatch)<F> for [DynamicIndices](#DynamicIndices) where F: Fn(usize, [ThreadInDomain](https://www.unum.cloud/docs/forkunion/rust/topology.md#ThreadInDomain)) + Sync`
- `impl<F> [ForDispatch](#ForDispatch)<F> for [StaticSlices](#StaticSlices) where F: Fn([TasksRange](#TasksRange), [ThreadInDomain](https://www.unum.cloud/docs/forkunion/rust/topology.md#ThreadInDomain)) + Sync`

## *

Runs `tasks_count` tasks through `function`, blocking until every thread finishes.

```rust
fn dispatch(pool: &mut ThreadPool, tasks_count: usize, function: &F) -> Result<()>
```

## StaticIndices

One call per index, statically partitioned across threads.

```rust
pub struct StaticIndices;
```

Implements: `Clone`, `Copy`, `Debug`, `[ForDispatch](#ForDispatch)<F>`

## DynamicIndices

One call per index, greedily stolen by whichever thread frees up first.

```rust
pub struct DynamicIndices;
```

Implements: `Clone`, `Copy`, `Debug`, `[ForDispatch](#ForDispatch)<F>`

## StaticSlices

One call per contiguous run of indices, statically partitioned across threads.

```rust
pub struct StaticSlices;
```

Implements: `Clone`, `Copy`, `Debug`, `[ForDispatch](#ForDispatch)<F>`

## Capabilities

Everything the library can do, whether decided when it was compiled or found on this machine.

```rust
pub struct Capabilities(pub u32);
```

One bit per facility, and two accessors ask two questions of the same bit.
[`comptime_capabilities`](#comptime_capabilities) reports whether the code was _built_: a set
[`Capabilities::PLACE_HUGE_PAGES_ON_DOMAIN`](#PLACE_HUGE_PAGES_ON_DOMAIN) means we compiled the path that asks for them.
[`runtime_capabilities`](#runtime_capabilities) reports whether the machine _offers_ it now.

Neither implies the other. A binary that built [`Capabilities::PLACE_MEMORY_ON_DOMAIN`](#PLACE_MEMORY_ON_DOMAIN) runs
perfectly well on a single-node box, where the runtime accessor never sets that bit; and a
machine with four NUMA nodes reports none of them to a build that left the topology out.

```
use forkunion::{comptime_capabilities, Capabilities};
if comptime_capabilities().contains(Capabilities::COLOCATE_POOLS_ON_DOMAIN) {
    // `spawn_in`, `DomainAllocator`, and friends are real here.
}
```

- `0` `u32`

Implements: `Clone`, `Copy`, `StructuralPartialEq`, `PartialEq`, `Eq`, `Hash`, `Debug`

## NONE

Nothing detected, or nothing compiled in.

```rust
pub const NONE: Capabilities = _;
```

## X86_PAUSE

x86 `pause` instruction.

```rust
pub const X86_PAUSE: Capabilities = _;
```

## X86_TPAUSE

x86-64 `tpause` instruction, with `WAITPKG` support.

```rust
pub const X86_TPAUSE: Capabilities = _;
```

## ARM64_YIELD

Arm `yield` instruction.

```rust
pub const ARM64_YIELD: Capabilities = _;
```

## ARM64_WFET

AArch64 `wfet` instruction, with `FEAT_WFxT` support.

```rust
pub const ARM64_WFET: Capabilities = _;
```

## RISCV64_PAUSE

RISC-V `pause` instruction.

```rust
pub const RISCV64_PAUSE: Capabilities = _;
```

## RISCV64_WRS

RISC-V `WRS.STO` monitored wait, from the `Zawrs` extension.

```rust
pub const RISCV64_WRS: Capabilities = _;
```

## OS_THREADS

Own the raw OS thread handle instead of a `std::thread`.

```rust
pub const OS_THREADS: Capabilities = _;
```

## TOPOLOGY

Enumerate this machine's cores, compute domains, and memory domains.

```rust
pub const TOPOLOGY: Capabilities = _;
```

## PLACE_THREADS_BY_AFFINITY

Bind a thread to a set of cores, choosing where it runs.

```rust
pub const PLACE_THREADS_BY_AFFINITY: Capabilities = _;
```

## PLACE_THREADS_BY_CORE_CLASS

Steer a thread onto a class of core at creation, choosing where it runs.

```rust
pub const PLACE_THREADS_BY_CORE_CLASS: Capabilities = _;
```

## RESCHEDULE_THREADS_BY_CLASS

Reclass a thread's scheduler to sleep or wake it, choosing when it runs.

```rust
pub const RESCHEDULE_THREADS_BY_CLASS: Capabilities = _;
```

## PLACE_MEMORY_ON_DOMAIN

Place a buffer's pages on a chosen memory domain.

```rust
pub const PLACE_MEMORY_ON_DOMAIN: Capabilities = _;
```

## PLACE_HUGE_PAGES_ON_DOMAIN

Place larger-than-base pages on a chosen memory domain.

```rust
pub const PLACE_HUGE_PAGES_ON_DOMAIN: Capabilities = _;
```

## HUGE_TRANSPARENT_PAGES

The kernel promotes base pages to huge pages on its own.

```rust
pub const HUGE_TRANSPARENT_PAGES: Capabilities = _;
```

## COLOCATE_POOLS_ON_DOMAIN

The domain-aware `colocated_pool` and `distributed_pool` are compiled in.

```rust
pub const COLOCATE_POOLS_ON_DOMAIN: Capabilities = _;
```

## X86_CLDEMOTE

`CLDEMOTE` moves a just-written line toward the shared LLC and retains it.

```rust
pub const X86_CLDEMOTE: Capabilities = _;
```

Reporting-only:
the emitter is chosen at compile time by `FORKUNION_WITH_DEMOTE_CACHE_LINES`, never
dispatched.

## ARM64_DC_CVAC

`DC CVAC` cleans a dirty line to the coherency point - AArch64's nearest demote.

```rust
pub const ARM64_DC_CVAC: Capabilities = _;
```

Set where
EL0 execution is known-legal, i.e. Linux, which sets `SCTLR_EL1.UCI`.

## RISCV64_ZICBOM

The kernel enabled user-mode Zicbom cache-block management, attested through `hwprobe` - the bit the C ABI's cascade selects `cbo.clean` on.

```rust
pub const RISCV64_ZICBOM: Capabilities = _;
```

## X86_CMPCCXADD

`CMPCCXADD`, the conditional atomic add, admitting `x86_cmpccxadd_atomic_ref`.

```rust
pub const X86_CMPCCXADD: Capabilities = _;
```

## X86_RAOINT

RAO-INT `aadd` and its family, executed at the shared cache.

```rust
pub const X86_RAOINT: Capabilities = _;
```

## ARM64_LSE

Armv8.1 `FEAT_LSE`: one instruction per read-modify-write.

```rust
pub const ARM64_LSE: Capabilities = _;
```

## ARM64_RCPC

Armv8.3 `FEAT_LRCPC`: `LDAPR` acquires, cheaper than `LDAR`'s.

```rust
pub const ARM64_RCPC: Capabilities = _;
```

## RISCV64_ZACAS

RISC-V Zacas, the `amocas` compare-and-swap.

```rust
pub const RISCV64_ZACAS: Capabilities = _;
```

## RISCV64_ATOMIC

The RISC-V A extension's `lr`, `sc` and `amo` words.

```rust
pub const RISCV64_ATOMIC: Capabilities = _;
```

## ARM64_PRFM

`PRFM`, AArch64's prefetch hint.

```rust
pub const ARM64_PRFM: Capabilities = _;
```

## RISCV64_ZICBOP

RISC-V Zicbop, the `prefetch.w` hint.

```rust
pub const RISCV64_ZICBOP: Capabilities = _;
```

## RISCV64_ZABHA

RISC-V Zabha, byte and halfword atomics.

```rust
pub const RISCV64_ZABHA: Capabilities = _;
```

## ALL

All-ones allow-mask: pass to a pool constructor to disable capability filtering.

```rust
pub const ALL: Capabilities = _;
```

## contains

Whether every bit of `other` is set in `self`.

```rust
pub const fn contains(self, other: Capabilities) -> bool
```

## CallerExclusivity

Defines whether the calling thread participates in task execution.

```rust
pub enum CallerExclusivity
```

- `Inclusive` = `0`: The calling thread participates in the workload, spawning N-1 workers
- `Exclusive` = `1`: The calling thread only coordinates, doesn't execute tasks, spawning N workers

Implements: `Copy`, `Clone`, `Debug`, `StructuralPartialEq`, `PartialEq`, `Eq`

## Status

Why a call into the C core failed, in the vocabulary the core itself uses.

```rust
pub enum Status
```

Mirrors `fu_status_t` value-for-value, so the FFI boundary only retypes. [`Unrecognized`](#Unrecognized) covers a
status a newer core reports and this build has no name for.

- `Unknown` = `-1`: No reason was reported, or one this build does not name.
- `BadAlloc` = `-2`: An allocation or mapping failed; a smaller request may succeed.
- `CapacityExhausted` = `-3`: A fixed ceiling was reached, so a smaller request will not help either.
- `InvalidArgument` = `-4`: An argument was malformed, out of range, or would overflow a byte count.
- `ConfigMismatch` = `-5`: The handles or the pool kind cannot serve this call together.
- `AlreadySpawned` = `-6`: The pool is already spawned; terminate it first.
- `NotSpawned` = `-7`: The pool was never spawned.
- `ThreadRefused` = `-8`: The OS declined to create a thread - a resource limit, or permissions.
- `TopologyUnavailable` = `-9`: The machine could not be described; transient if its CPU set changed mid-probe.
- `PermissionDenied` = `-10`: A privileged operation was declined.
- `Unsupported` = `-11`: This build or this machine has no such facility.
- `Contended` = `-14`: The pool is running another dispatch: another caller's, or the one this call runs inside.
- `Unrecognized` = `i32::MIN`: A status this binding has no name for.

Implements: `Clone`, `Copy`, `StructuralPartialEq`, `PartialEq`, `Eq`, `Debug`, `Hash`

## describe

Static, English description; mirrors `fu_status_to_string`.

```rust
pub fn describe(self) -> &'static str
```

## Error

A failure, whether the core reported it or the binding caught it before the call.

```rust
pub struct Error
```

`detail` names the symbol or the argument, which is what turns a status into a diagnosis.
Both fields are plain data, so nothing allocates on the failure path - which is what lets this
crate stay `no_std`.

- `status` `Status`: The identity of the failure, shared with the C interface.
- `detail` `&'static str`: The detail the status alone cannot carry.

Implements: `Clone`, `Copy`, `StructuralPartialEq`, `PartialEq`, `Eq`, `Debug`, `Display`, [`Error`](#Error)

## TasksRange

A half-open `[first, first + count)` run of task indices - the "what work" of a slice dispatch.

```rust
pub struct TasksRange
```

Iterable, so a callback reads `for task in range` rather than rebuilding the bounds. An idle
thread receives a range with `count == 0`, which every dispatch still calls exactly once.

- `first` `usize`: The first task index in the run.
- `count` `usize`: How many tasks the run covers; zero means an idle thread.

Implements: `Copy`, `Clone`, `Debug`, `StructuralPartialEq`, `PartialEq`, `Eq`, `IntoIterator`

## end

One past the last task index, so `first..end()` is the half-open span.

```rust
pub fn end(&self) -> usize
```

## is_empty

Whether the run covers no tasks at all.

```rust
pub fn is_empty(&self) -> bool
```

## range

The run as a `Range`, for indexing a slice or driving a `for` loop.

```rust
pub fn range(&self) -> Range<usize>
```

## SyncConstPtr

A thread-safe wrapper around raw pointers for sharing read-only data across threads.

```rust
pub struct SyncConstPtr<T>
```

This type is designed for scenarios where you need to share immutable data across async tasks or
threads, particularly when the standard borrowing rules would prevent such sharing. The caller
is responsible for ensuring that:

- The pointed-to data remains valid for the lifetime of use

- The data is not modified while being accessed through [`SyncConstPtr`](#SyncConstPtr)

Safety: This type is marked as `Send + Sync` but requires careful usage:

- Only use with data that won't be modified during the lifetime of the pointer

- Ensure the pointed-to data outlives all uses of the [`SyncConstPtr`](#SyncConstPtr)

- The [`get`](https://www.unum.cloud/docs/forkunion/rust/busy-waiting.md#get) method is unsafe and requires the caller to ensure bounds checking

Examples: ```
use forkunion::*;

let data = vec![1, 2, 3, 4, 5];
let sync_ptr = SyncConstPtr::new(data.as_ptr());

// Safe to use in async contexts
let value = unsafe { sync_ptr.get(0) };
assert_eq!(*value, 1);
```

Implements: `Clone`, `Copy`, `Debug`, `Send`, `Sync`

## new

Wraps a raw pointer; every obligation is discharged at [`get`](https://www.unum.cloud/docs/forkunion/rust/busy-waiting.md#get).

```rust
pub fn new(ptr: *const T) -> Self
```

## get

Gets a reference to the element at the given index.

```rust
pub unsafe fn get(&self, index: usize) -> &T
```

#### Returns

A reference to the element at the given index.

Safety: The caller must ensure that:

- The index is within bounds of the allocated data

- The data at the index is properly initialized

- The data remains valid for the lifetime of the returned reference

Arguments: - `index` - The index of the element to access

## as_*_ptr

Returns the raw pointer.

```rust
pub fn as__ptr(&self) -> *const T
```

## SyncMutPtr

A mutable raw pointer that is `Send` and `Sync`, for disjoint writes from many threads.

```rust
pub struct SyncMutPtr<T>
```

Implements: `Clone`, `Copy`, `Send`, `Sync`

## new

Wraps a raw pointer; every obligation is discharged at [`get`](https://www.unum.cloud/docs/forkunion/rust/busy-waiting.md#get).

```rust
pub const fn new(ptr: *mut T) -> Self
```

## get

Returns a mutable pointer to the element at the given index.

```rust
pub unsafe fn get(&self, index: usize) -> *mut T
```

Safety: The caller must ensure:

- The index is within the bounds of the original allocation

- No overlapping mutable access occurs from multiple threads

- Each thread accesses disjoint indices when used concurrently

- The pointer remains valid for the duration of access

## IndexedSplit

Splits a range of tasks into fair-sized runs for parallel distribution.

```rust
pub struct IndexedSplit
```

The first `(tasks % threads)` runs have size `ceil(tasks / threads)`.
The remaining runs have size `floor(tasks / threads)`.

This ensures optimal load balancing across threads with minimal size variance.
See: [https://lemire.me/blog/2025/05/22/dividing-an-array-into-fair-sized-chunks/](https://lemire.me/blog/2025/05/22/dividing-an-array-into-fair-sized-chunks/)

Implements: `Debug`, `Clone`

## new

Creates a new indexed split for distributing tasks across threads.

```rust
pub fn new(tasks_count: usize, threads_count: usize) -> Self
```

Arguments: - `tasks_count` - Total number of tasks to distribute

- [`threads_count`](#threads_count) - Number of threads to distribute across (must be > 0)

Panics: Panics if [`threads_count`](#threads_count) is zero.

## get

Returns the range for a specific thread index.

```rust
pub fn get(&self, thread_index: usize) -> Range<usize>
```

## spawn

Spawns a pool with the specified number of threads.

```rust
pub fn spawn(topology: &Topology, threads: usize) -> ThreadPool
```

## named_spawn

Spawns a named pool with the specified number of threads.

```rust
pub fn named_spawn(topology: &Topology, name: &str, threads: usize) -> ThreadPool
```

## for_n

Standalone function to distribute `n` similar duration calls between threads.

```rust
pub fn for_n<F>(pool: &mut ThreadPool, n: usize, function: F) -> Result<()>
where
    F: Fn(usize, ThreadInDomain) + Sync,
```

## for_n_dynamic

Standalone function to execute `n` uneven tasks on all threads.

```rust
pub fn for_n_dynamic<F>(pool: &mut ThreadPool, n: usize, function: F) -> Result<()>
where
    F: Fn(usize, ThreadInDomain) + Sync,
```

## for_slices

Standalone function to distribute `n` tasks in slices.

```rust
pub fn for_slices<F>(pool: &mut ThreadPool, n: usize, function: F) -> Result<()>
where
    F: Fn(TasksRange, ThreadInDomain) + Sync,
```

## for_each_task_mut

Helper function to visit every element exactly once with mutable access.

```rust
pub fn for_each_task_mut<T, F>(pool: &mut ThreadPool, data: &mut [T], function: F) -> Result<()>
where
    T: Send,
    F: Fn(&mut T, usize, ThreadInDomain) + Sync,
```

## for_each_task_mut_dynamic

Helper function to visit every element exactly once with dynamic work-stealing.

```rust
pub fn for_each_task_mut_dynamic<T, F>(
    pool: &mut ThreadPool,
    data: &mut [T],
    function: F,
) -> Result<()>
where
    T: Send,
    F: Fn(&mut T, usize, ThreadInDomain) + Sync,
```

## version_*

Returns the major version number of the ForkUnion library.

```rust
pub fn version_major() -> usize
```

- `version_major`
- `version_minor`
- `version_patch`

## version

Returns the library version as a tuple of (major, minor, patch).

```rust
pub fn version() -> (usize, usize, usize)
```

## comptime_capabilities

Which kernel facilities this build of ForkUnion was compiled to use.

```rust
pub fn comptime_capabilities() -> Capabilities
```

## name_capabilities

Formats a capability bitset into a comma-separated name list, like `"threads,topology"`.

```rust
pub fn name_capabilities(caps: Capabilities) -> Result<String>
```

POLISH: returns an owned `String`, since `fu_name_capabilities` writes into a caller buffer, not
a static pointer; a fixed-capacity stack type would avoid this allocation.

## comptime_capabilities_string

The set [`comptime_capabilities`](#comptime_capabilities) bits, comma-separated, like `"threads,topology"`.

```rust
pub fn comptime_capabilities_string() -> Result<String>
```

## runtime_capabilities

Which features this machine turned out to offer, probing the CPU and the memory system.

```rust
pub fn runtime_capabilities() -> Capabilities
```

## runtime_capabilities_string

The set [`runtime_capabilities`](#runtime_capabilities) bits, comma-separated, like `"arm64_yield,numa_aware"`.

```rust
pub fn runtime_capabilities_string() -> Result<String>
```

## bytes_for_elements

Bytes occupied by [`count`](https://www.unum.cloud/docs/forkunion/rust/parallel-iterators.md#count) elements of `element_bytes` each, refusing a product that would wrap.

```rust
pub fn bytes_for_elements(count: usize, element_bytes: usize) -> Result<usize>
```

## DEFAULT_ALIGNMENT

Default alignment for preventing false sharing between threads.

```rust
pub const DEFAULT_ALIGNMENT: usize = 128usize;
```

Picked from the target the way `FORKUNION_DEFAULT_ALIGNMENT` is in `types.hpp`: 256 bytes on
s390x, 64 on wasm, 128 elsewhere.

On x86, most CPUs fetch 2 cache lines (128 bytes) at once with spatial prefetching enabled.
This conservative padding prevents false sharing even with aggressive prefetch settings.
Only padding rests on this - a thread pool strides its cells by the width the machine itself
reports at spawn, which is exact where this is a guess.

