std/task/pool

std/task/src/pool.trb

A pool of resources, written once over Permits: at most maximum of them open at once, the idle ones kept for the next Pool.acquire, and a caller beyond maximum waits, oldest first.

const pool = Pool.of(4, { connect(address) })
using lease = pool.acquire().await()?
lease.resource.send(request).await()?
lease.giveBack()

Reach for it wherever opening a resource is dear and holding too many is harmful: connections to a server, handles of a device, processes of a helper. What the pool does not know stays with the caller: a key per origin (keep one Pool for each), a timeout (pool.acquire().within(limit), as Permits.acquire is a task), and what happens to a resource while it is lent out.

Pitfalls

type PoolError

type PoolError<Failure>

Why Pool.acquire gave no resource: the pool was stopped, or opening a new one failed with failure.

case Closed

case Closed

The pool was stopped, before the call or while it waited.

case Open

case Open(failure: Failure)

There was no idle resource and opening a new one failed; the slot is free again.

extend PoolError<Failure> with Show

extend<Failure: Show> PoolError<Failure> with Show

The error in words: the pool is closed, or could not open a resource: and the failure.

fn show

fn show(): String

type Pool

shared type Pool<Resource, Failure>

A pool of at most maximum resources. See the module comment for when to use it; the pool is shared, so every holder of it sees the same slots.

field maximum

maximum: Int

How many resources may be open at once: what it was made with, taken as 1 where that was below 1.

fn of

static fn of(maximum: Int, open: () => Task<Result<Resource, Failure>>, reusable: (resource: Resource) => Bool = { _ => true }): Self

A pool of maximum resources, opened by open when an Pool.acquire finds no idle one. reusable tells whether an idle resource may be lent again; it answers true for every resource unless the pool is made with another. A maximum below 1 is taken as 1.

fn acquire

var fn acquire(): Task<Result<Lease<Resource>, PoolError<Failure>>>

Waits for a free slot, then lends the newest idle resource that is reusable - the others found on the way are dropped - or opens a new one. A failed open gives the slot back and answers PoolError.Open; a pool that is stopped answers PoolError.Closed, also where it was stopped while the caller waited. A caller cancelled while it waits takes no slot.

fn stop

var fn stop()

Drops the idle resources; what is returned later is dropped, and every later Pool.acquire answers Closed.

fn close

var fn close()

What the release of the pool runs: the same as Pool.stop.

fn idleCount

fn idleCount(): Int

How many resources are idle now.

fn openCount

fn openCount(): Int

How many resources are open now: the idle ones and those that are lent out.

type Lease

shared type Lease<Resource> with Close

One caller's hold on a slot and on the resource in it. It is settled exactly once, however often it is asked: given back to the pool by Lease.giveBack, or dropped by Lease.discard - and the release of the lease discards where neither ran, so a caller that fails or is cancelled gives its slot back.

field resource

resource: Resource

The resource that is lent.

fn giveBack

var fn giveBack()

The resource is good: kept for the next caller where the pool is running and reusable agrees; the slot is free.

fn discard

var fn discard()

The resource is dropped, and the slot is free.

fn close

var fn close()

Discards what neither Lease.giveBack nor Lease.discard settled: what the release of the lease runs.