Skip to main content

Crate napi_async_runtime

Crate napi_async_runtime 

Source
Expand description

A shared, tokio-free async runtime for napi-rs addons.

Two layers, one crate:

  • Scheduler (always compiled, napi-free): a pluggable async/CPU/blocking scheduler with a MultiThread flavor (Rayon-backed worker pool on native targets) and a CurrentThread flavor (host-driven turns; the only flavor on WebAssembly). Exposed at the crate root: spawn, spawn_blocking, block_on, sleep_until, configure, start, shutdown, the host driver SPIs (CurrentThreadTaskDriver, TimerDriver), and the metrics/introspection helpers.
  • napi adapter (feature napi, on by default): an unsafe impl AsyncRuntime backend for napi-rs’ async-runtime SPI plus the JavaScript-facing host protocol (registerCurrentThreadTaskHost, registerTimerHost, config/metrics exports). Hosts call install from their own #[napi_derive::module_init] hook after resolving their runtime configuration; this crate deliberately ships no module_init of its own.

Modules§

js_callback
Generic JavaScript-callback alias used by the host protocol exports.

Structs§

BindingHostRegistration
BindingRuntimeConfig
BindingRuntimeMetrics
BindingRuntimeOptions
BlockOnDeadlock
Typed panic payload for a self-detected block_on deadlock. Thrown with std::panic::panic_any at the park DECISION (never merely on a Pending return, because a self-waking future is not a deadlock), so the runtime fails loudly instead of freezing the thread (and, in the CurrentThread-on-JS-thread case, the whole JS event loop, where not even test-harness timeouts can fire). When the panic unwinds out of a spawned task’s poll, JoinError::from_panic preserves this diagnostic’s message.
CurrentThreadTaskCallbackLease
Retains one admitted CurrentThread host callback in its runtime generation.
CurrentThreadTaskDelivery
CurrentThreadTaskDriverId
JoinError
JoinHandle
RuntimeConfigError
RuntimeMetricsSnapshot
RuntimeOptions
RuntimeOptionsPatch
A partial update to the runtime options exposed by the binding.
Sleep
Timer future returned by sleep_until. Resolves at/after its deadline (or early on runtime shutdown drain-fire). Dropping it cancels the underlying registration – the tokio::select! losing-arm semantics the watch coordinator’s debounce loop relies on.
TimerDriverId
Handle to one registration in a TimerDriverRegistry, returned by TimerDriverRegistry::register and consumed by TimerDriverRegistry::unregister.
TimerDriverRegistry
Host timer drivers keyed by registration order. A driver is owned by a host context that can die independently of the process – the Node binding registers one driver per importing napi env, and a worker’s env teardown kills its driver’s threadsafe function. A single first-wins slot is therefore unsound: a worker that imports the binding first and then exits would permanently shadow the slot with a dead driver, and every CT timer armed afterwards would busy-fail against it. Selecting only the newest live driver is also insufficient: a live worker can be CPU-blocked indefinitely while another environment remains responsive. The registry therefore keeps all registrants and returns a stable snapshot of every live driver at each Sleep poll, sweeping dead entries out as it goes.

Enums§

BindingRuntimeFlavor
BlockOnDeadlockKind
Which self-detected block_on deadlock class fired.
RuntimeFlavor

Constants§

DEFAULT_DRAIN_LINGER_MICROS
Default drainer idle-linger budget (µs): RuntimeOptions::drain_linger’s default, applied when no host configuration or DRAIN_LINGER_ENV override resolves a different value.
DRAIN_LINGER_ENV
Environment variable overriding the MultiThread drainer idle-linger budget in MICROSECONDS (RuntimeOptions::drain_linger, consumed by MultiThreadExecutor::drain). After a drainer runs the runnable/blocking queues empty it parks on its own [DriverParker] (registered with parked_drivers) for up to this long before returning its thread to rayon. A wave of work arriving within the budget is resumed by ONE targeted condvar wake instead of a spawn_fifo respawn through rayon’s idle protocol – whose per-transition cost (a wake cascade plus 33 sched_yield rounds and a full steal sweep on EVERY worker that was woken) is what makes system CPU scale with the worker count under wave-shaped loads. 0 disables lingering (legacy exit-and-respawn on every empty queue); values above MAX_DRAIN_LINGER_MICROS are clamped. A frame’s TOTAL lingering residence is additionally capped at [DRAIN_LINGER_FRAME_FACTOR] times this budget so periodic sub-budget work cannot pin a rayon worker inside one drain frame forever.
MAX_ASYNC_RUNTIME_WORKER_THREADS
Maximum number of physical workers a scheduler owned by this crate may create.
MAX_DRAIN_LINGER_MICROS
Ceiling on RuntimeOptions::drain_linger in MICROSECONDS (100ms). Validation (and, defensively, executor construction) clamps the budget here: lingering exists to bridge micro-scale gaps between scheduling waves, and a lingering drainer holds a pool worker plus an active_drainers slot while idle, so budgets beyond this bound stop tuning the wave optimization and start pinning workers.
PARK_DEADLINE_ENV
Environment variable that arms deadline-based block_on deadlock detection: milliseconds a runtime-owned park may sleep with zero runtime progress before panicking (see RuntimeOptions::park_deadline). Missing, non-numeric or 0 all mean “disabled”. The runtime does NOT read this variable itself: the embedding host’s env-resolution parses it once at addon load and hands the result to configure through RuntimeOptions::park_deadline. The name is the conventional default; hosts may resolve their own variable into the same field.

Traits§

CurrentThreadTaskDriver
Host-turn dispatcher for the CurrentThread runnable queue.
TimerDriver
Seam for host-delegated timers: the CurrentThread flavor cannot park a helper thread on a threadless build, so it delegates sleep_until to the host event loop through this trait. The Node binding installs a setTimeout-based implementation via register_timer_driver at import – one per importing napi env (main thread AND workers).

Functions§

acknowledge_current_thread_task_delivery
Acknowledge that one exact host delivery callback returned the successful claim result from drive_current_thread_tasks.
block_on
block_on_dyn
cancel_current_thread_task_dispatch
Cancel an accepted CurrentThread host delivery that failed before it could schedule a fresh turn.
configure
configure_async_runtime
Override the shared async runtime’s flavor and thread counts.
configure_partial
configured_options
drive_current_thread_tasks
Poll the shared runtime’s CurrentThread queue from a host-dispatched turn.
fail_current_thread_task_delivery
Report that one exact host delivery callback failed.
get_async_runtime_config
Return the effective async runtime configuration.
get_async_runtime_metrics
Return a snapshot of the shared async runtime’s task and scheduler counters.
get_current_thread_task_host_contract_version
Return the native CurrentThread task-host ABI expected by the JavaScript package before it invokes either async-runtime host registration. Version 4 reserves and validates an exact registration capability before host installation performs side effects.
has_live_timer_driver
Whether a LIVE host timer driver is currently registered. Lets the embedder report CurrentThread timer availability honestly (e.g. through a capabilities export) instead of guessing: with no live driver a CurrentThread sleep_until would panic, so the capability must read false – including when the only registrants are DEAD (their owning envs torn down), not merely when none ever registered.
install
Configure the shared scheduler and register it as this addon’s napi AsyncRuntime backend.
is_current_thread_host_registration_active
Return whether one exact CurrentThread task- or timer-host registration is still live. The JavaScript package revalidates its process-global marker on every module evaluation so native eviction cannot leave a stale installed bit that permanently suppresses replacement registration.
is_multi_threaded
max_async_runtime_worker_threads
Platform-realizable worker ceiling after applying this crate’s production cap.
metrics
register_current_thread_task_driver
register_current_thread_task_host
Install a native-owned host turn used to poll CurrentThread runnables without re-entering arbitrary future waker locks. Called once per importing environment. JavaScript callbacks are rejected synchronously.
register_timer_driver
Register a host timer driver for the CurrentThread flavor’s sleep_until. Every host context that can serve timers registers its own driver (the Node binding: one per importing napi env); every LIVE registrant receives each timer so a starved environment cannot own its only wake source. Returns the handle for unregister_timer_driver.
register_timer_host
Install the host timer callback backing the shared async runtime’s CurrentThread timers. Called at import by every binding-loading JS entry with paired setTimeout/clearTimeout callbacks; each importing env (main thread and workers alike) registers its own host, and every live host receives each timer.
request_current_thread_task_drain
Request service for work that may have accumulated while no live host dispatcher was registered. A new host receives the existing internal dispatch capability through a fresh registration-scoped delivery token; already accepting hosts keep their attempts.
reserve_current_thread_host_registration
Reserve an exact CurrentThread host registration capability before either task or timer installation performs side effects. The JavaScript package validates the returned words and passes them back to one registration call.
reset_async_runtime_metrics
Reset cumulative async runtime event counters to zero.
reset_metrics
resolve_drain_linger
Resolve an env-derived drainer idle-linger budget (a raw [DRAIN_LINGER_ENV]-style value, MICROSECONDS) into RuntimeOptions::drain_linger: a missing or non-numeric value keeps fallback (the host-provided field, [DEFAULT_DRAIN_LINGER_MICROS] by default); 0 disables lingering; anything else is clamped to [MAX_DRAIN_LINGER_MICROS] so an extreme value (e.g. u64::MAX) tunes the wave optimization instead of pinning pool workers on never-expiring lingers. RuntimeOptions validation re-applies the same clamp, so a host bypassing this resolver cannot smuggle an unbounded budget either.
resolve_thread_count
Resolve an env-derived thread count: parse the raw value, treat a missing, non-numeric, OR zero value as “unset” and fall back to default, then clamp the result to maximum. A 0 must never survive here because the host’s module-init path feeds this count into a constructor that rejects 0: RuntimeOptions validation rejects a 0 thread count, which would panic an expect() during addon load over what may be a simple typo in an environment variable.
shutdown
sleep_until
Sleep until deadline on the runtime’s timer facility. MultiThread uses the executor-owned heap; CurrentThread requires a LIVE host driver registered via register_timer_driver and otherwise fails LOUD (a missing driver must never become a silent never-firing debounce).
spawn
spawn_blocking
spawn_detached
start
try_block_on_dyn
Drive a borrowed future without consuming it when admission or shutdown fails.
try_spawn
Submit a future without consuming it when the runtime is not accepting work.
try_spawn_blocking
Submit blocking work without consuming it when the runtime is not accepting work.
try_spawn_detached
unregister_current_thread_task_driver
unregister_current_thread_task_host
Evict exactly one native host installed by registerCurrentThreadTaskHost. Managed workerd disposal uses this before environment cleanup so a later throwing cleanup hook cannot leave the process-global driver selected.
unregister_timer_driver
Remove a driver registered via register_timer_driver – called when its host context dies (the Node binding evicts on env teardown and on callback failure). Idempotent.
unregister_timer_host
Evict exactly one callback installed by registerTimerHost. Pending sleeps are woken so they can reselect another live environment.

Type Aliases§

TimerId