some_global_executor 0.1.6

Reference thread-per-core executor for the some_executor crate.
Documentation
A thread-pool executor for async Rust that runs the same code on OS threads natively
and on web workers on wasm32.

![logo](art/logo.png)

This crate is the reference executor for the `some_executor` framework: if you write
code against the `SomeExecutor` trait and want one real executor to run it
everywhere, this is it.

# Why this crate exists

Rust ships no async executor in std, and no trait for executors to implement, so most
async code is written directly against one specific runtime. The `some_executor`
crate fixes this by defining a small interface between the code that *has* futures and
the code that *runs* them: libraries spawn onto "some" executor through a trait, and
applications decide which executor that actually is.

That framework needs a production-quality executor to point at, and this crate is it.
`some_global_executor` implements the `SomeExecutor` trait for `Send + 'static`
futures with a resizable pool of workers, and can install itself as the process-wide
global executor or as a per-thread executor — which is exactly what
`some_executor`'s `current_executor()` discovery hierarchy looks for. Out of the
box, `some_executor` falls back to a built-in toy executor that warns when used;
installing this crate is the intended way to replace it.

# What it does

- **One API, two platforms.** On native targets, tasks run on a pool of OS threads fed
  by a `crossbeam-channel` queue. On wasm32, the same public API runs tasks on web
  workers, with a custom async channel for distribution (workers can't block). The
  platform switch is conditional compilation; your code doesn't change.
- **Task observation.** Spawning returns an observer from the `some_executor`
  framework: await it for the result, poll it, detach it, or drop it to request
  cooperative cancellation.
- **Graceful shutdown.** `Executor::drain` blocks until every spawned task has
  finished; `Executor::drain_async` is the awaitable version for async contexts.
- **Runtime resizing.** `Executor::resize` grows or shrinks the worker pool while
  the executor is running.
- **Structured logging.** Internal operations are logged through
  [`logwise`]https://github.com/drewcrawford/logwise, with optional diagnostic,
  forensic, and performance feature flags. An optional `exfiltrate` feature exposes a
  registry of live pools for external debugging tools.

# Quick start

```
use some_global_executor::Executor;
use some_executor::SomeExecutor;
use some_executor::task::{Task, Configuration};
# if cfg!(target_arch = "wasm32") { return; }

// A named pool with 4 worker threads (or web workers on wasm32).
let mut executor = Executor::new("my-executor".to_string(), 4);

let task = Task::without_notifications(
    "example-task".to_string(),
    Configuration::default(),
    async { 42 }
);

let observer = executor.spawn(task);
// Await the observer for the result, or drop it to cancel the task.

// Optionally make this the executor that some_executor's
// current_executor() / global spawning resolves to:
executor.set_as_global_executor();

// Block until all spawned tasks finish.
executor.drain();
```

# Where it fits in the ecosystem

The `some_executor` family divides the work like this:

- `some_executor` — the trait layer. Defines `Task`,
  observers, cancellation, priorities, task-locals, and the global/thread-local
  executor registry. No real scheduling of its own beyond a fallback.
- **`some_global_executor`** (this crate) — the reference executor for `Send` tasks:
  a thread pool natively, web workers on wasm32.
- [`some_local_executor`]https://github.com/drewcrawford/some_local_executor — runs
  non-`Send` tasks on the current thread, and can receive tasks from other threads.
- [`test_executors`]https://github.com/drewcrawford/test_executors — toy executors
  for unit tests.

So: libraries depend on `some_executor` and stay executor-agnostic; applications
depend on this crate (or another implementation) and install it once at startup.

# Alternatives

If you aren't committed to the `some_executor` interface, the field looks like this:

| Crate | What it is | Trade-off vs. this crate |
|---|---|---|
| [tokio]https://crates.io/crates/tokio | Full runtime: executor plus async I/O, timers, sync primitives, and a huge ecosystem | The default choice if you need its I/O stack — but your code becomes tokio-specific, and there is no multithreaded wasm32 story. This crate is executor-only and trait-first. |
| [async-executor]https://crates.io/crates/async-executor (smol) | Small, composable executor; bring your own reactor | Closest in spirit (executor without a bundled runtime), but no common spawning trait for libraries, no observer/cancellation model, and no web-worker pool on wasm32. |
| [futures-executor]https://crates.io/crates/futures-executor | The minimal `ThreadPool`/`LocalPool` in the futures crate | Fine for gluing a future into sync code; no task metadata, draining, resizing, or wasm parallelism. |
| [wasm-bindgen-futures]https://crates.io/crates/wasm-bindgen-futures | `spawn_local` onto the browser event loop | The standard wasm answer, but single-threaded and wasm-only. This crate gives you actual parallelism via web workers and the same API natively. |
| [rayon]https://crates.io/crates/rayon | Data-parallelism for synchronous code | Not an async executor at all; complementary rather than competing. |
| async-std | Formerly a full alternative runtime | Discontinued (deprecated in 2025); its maintainers point to smol. |
| `some_executor`'s built-in fallback | Zero-config executor included in the trait crate | Always available so libraries work with no setup, but deliberately not production quality — it exists to be replaced by this crate. |

Pick this crate when you want executor-agnostic code (or already have it via
`some_executor`), need the same spawning code to work natively and on wasm32 with
real parallelism, and don't need a bundled I/O reactor. Pick tokio when you're
building on its networking stack and wasm isn't a target.

# More examples

## Observing task progress

Tasks can be observed to monitor their execution state:

```
use some_global_executor::Executor;
use some_executor::SomeExecutor;
use some_executor::task::{Task, Configuration};
use some_executor::observer::{Observer, Observation};
# if cfg!(target_arch = "wasm32") { return; }

let mut executor = Executor::new("observer-example".to_string(), 2);

let task = Task::without_notifications(
    "monitored-task".to_string(),
    Configuration::default(),
    async { "result" }
);

let observer = executor.spawn(task);

// Poll the observer to check task state
loop {
    match observer.observe() {
        Observation::Ready(value) => {
            println!("Task completed with: {}", value);
            break;
        }
        Observation::Pending => {
            // Task still running
            std::thread::yield_now();
        }
        _ => break,
    }
}

executor.drain();
```

## Global executor pattern

Set an executor as the global default for the application:

```
use some_global_executor::Executor;
# if cfg!(target_arch = "wasm32") { return; }

// Create and configure the global executor
let executor = Executor::new("global".to_string(), num_cpus::get());
executor.set_as_global_executor();

// Now tasks can be spawned using the global executor from anywhere
// in the application without passing executor references

# executor.drain();
```

## Dynamic thread pool management

Adjust executor capacity based on workload:

```
use some_global_executor::Executor;

let mut executor = Executor::new("dynamic".to_string(), 2);

// Scale up for heavy workload
executor.resize(8);

// Scale down during idle periods
executor.resize(2);

executor.drain();
```

# Performance considerations

- Thread pool sizing: Default to `num_cpus::get()` for CPU-bound work
- For I/O-bound tasks, consider using more threads than CPU cores
- WASM targets have platform-specific limitations on parallelism
- Use `drain_async()` in async contexts to avoid blocking

# Logging

This crate uses the `logwise` framework for structured logging. Internal operations
are logged at various levels for debugging and monitoring:

```
# use some_global_executor::Executor;
// Executor creation and operations are automatically logged
let executor = Executor::new("logged-executor".to_string(), 4);
// Logs: "Creating executor with name logged-executor and 4 threads"
# executor.drain();
```

# Requirements

Rust 1.95+ (edition 2024). Native targets need only stable Rust; running the wasm32
test suite requires nightly.