Skip to content

Reuse workers with TaskPool

NativeBrowserNode2 min read

Use a pool for CPU work that should execute in parallel. Use structured local async children for concurrent I/O.

At a glance

let pool = TaskPool(4);
let result = pool.dispatch(() => 3 * 3).await.unwrap();
pool.close().await.unwrap();
  • A pool reuses a fixed number of workers for parallel CPU jobs.
  • Close the pool when you no longer need it.
import system;
import system.task;

async fun main(): void {
    let pool = TaskPool(4);
    let result = pool.dispatch(() => 3 * 3).await.unwrap();
    System.println(result);
    let values = pool.map<int, int>([1, 2, 3], (value) => value * value).await.unwrap();
    pool.close().await.unwrap();
}

dispatch accepts synchronous worker code; dispatch_async accepts an async callback. Both return Result<T, TaskPoolError>. They check cancellation before submission and after the job settles. Async worker jobs inherit the submitted token. Long synchronous CPU jobs must capture and check a token explicitly to stop early.

map and map_async keep at most size dispatches active and preserve input order. Their failures use TaskFailure<TaskPoolError>. Worker captures and result types follow the sharing rules.

close().await stops accepting work, waits for active dispatches, and closes workers. Repeated closure succeeds. A nonpositive pool size creates no workers and dispatch returns InvalidSize; dispatch after closure returns Closed. A constructed dispatch future is lazy, so close before starting it rejects its later submission.

Use scope.spawn_on(pool, body) when a region should own a CPU job alongside local async work. Native worker replies wake the owner scheduler instead of blocking it.

TaskPool members

Member Returns Effect
TaskPool(size) TaskPool Starts size reusable workers
.size int Number of workers in the pool
.dispatch(body, token) Future<Result<T, TaskPoolError>> Runs a synchronous body on a worker
.dispatch_async(body, token) Future<Result<T, TaskPoolError>> Runs an async body on a worker
.map(items, body, token) Future<Result<U[], TaskFailure<TaskPoolError>>> Runs a synchronous body per item, at most size at once, in input order
.map_async(items, body, token) Future<Result<U[], TaskFailure<TaskPoolError>>> Same as map, with an async body
.close() Future<Result<Unit, TaskPoolError>> Stops accepting work, waits for active jobs, and shuts the workers down

The token argument is optional; without it, the pool uses the token inherited from the enclosing task scope.

TaskPoolError choices

Choice Returned when
InvalidSize The pool was created with a size of 0 or less
Closed Work was submitted after close()
Cancelled Cancellation was requested; any submitted work has settled first