Menu
Reference
- Manticore\Ds
- Async
- Classes
- AggregateError
- CancellationToken
- CancelledException
- Channel
- ChannelClosedException
- Context
- DeadlockException
- Mutex
- Once
- Pool
- Received
- SelectCase
- Selected
- Semaphore
- Settled
- Task
- TaskGroup
- TimeoutException
- Functions
- async()
- awaitAll()
- awaitAllSettled()
- awaitAny()
- channel()
- delay()
- dump()
- dumpOn()
- failure()
- group()
- mapConcurrent()
- mapSettled()
- readFile()
- select()
- selectNow()
- selectWithin()
- shield()
- shutdownOn()
- spawn()
- stats()
- timeout()
- watchdog()
- writeFile()
- Http
- Http\WebSocket
- Buffer
- Process
- Io\Poll
- Manticore\Sapi
- Ffi
Pool
final class Pool<T>
A bounded, FIFO pool of connections (or any objects), shared by tasks.
At most $max live objects exist at any time. A task that wants one beyond that parks until an object comes back; waiters are served in arrival order and a newcomer never overtakes a waiter. A returned object or freed capacity is handed to the first live waiter, so a waiter that is cancelled or times out neither consumes a slot nor loses a wake-up. Objects are created lazily by the factory, on the first acquire() that needs one.
$pool = new Async\Pool(fn() => new Conn(), 10);
$rows = $pool->with(fn($c) => $c->query('select 1'));Optional hooks keep the objects healthy: $reset runs on every release() (under shield(), so it may suspend), $ping validates an idle object before it is handed out, $maxLifetime and $idleTimeout retire old objects, and $close is called on every object the pool throws away. Objects only: the pool keys its books by object id. Pool::pdo() builds a pool of PDO connections.
Prefer with(), which releases in a finally. with() discards, instead of returning, the object when its body was cancelled or failed with a PDOException whose SQLSTATE is class 08 (the connection is gone or half-read).
Example
use Async\Pool;
use Async\TimeoutException;
use function Async\async;
use function Async\awaitAll;
use function Async\delay;
use function Async\spawn;
final class Connection
{
public static int $made = 0;
public int $id;
public function __construct()
{
$this->id = ++self::$made;
}
}
async(function () {
// At most two connections ever exist; waiters are served in arrival order.
$pool = new Pool(fn() => new Connection(), 2);
$jobs = [];
for ($i = 1; $i <= 4; $i++) {
$jobs[] = spawn(fn() => $pool->with(function (Connection $c) {
delay(0.01);
return $c->id;
}));
}
echo implode(",", awaitAll(...$jobs)), "\n";
$stats = $pool->stats();
echo "created {$stats['created']}, idle {$stats['idle']}, busy {$stats['busy']}\n";
// Bound the wait for a free connection with a timeout.
$a = $pool->acquire();
$b = $pool->acquire();
try {
$pool->acquire(0.01);
} catch (TimeoutException $e) {
echo "no connection free\n";
}
$pool->release($a);
$pool->discard($b);
echo "discarded: {$pool->stats()['discarded']}\n";
$pool->close();
});Output
1,2,1,2
created 2, idle 2, busy 0
no connection free
discarded: 1See also: Semaphore
Methods
__construct() | Create a pool. |
pdo() | A pool of PDO connections. |
acquire() | Take an object, creating one while there is capacity, else park FIFO until one is free. |
release() | Return an object to the pool. |
discard() | Throw an object away instead of returning it, because it is broken or its state is unknown. |
with() | Acquire an object, run $fn($object), and release it in a finally. |
close() | Close the pool. |
stats() | Counters for monitoring the pool. |
__construct()#source
function __construct(
callable $factory,
int $max,
?callable $reset = null,
?callable $ping = null,
float $maxLifetime = 0.0,
float $idleTimeout = 0.0,
?callable $close = null
)
Create a pool.
Parameters
$factorycallable- creates a new object; must return an object
$maxint- maximum number of live objects, at least 1
$reset?callable = null- called with the object on every
release(); if it throws, the object is discarded $ping?callable = null- called with an idle object before
acquire()hands it out; afalseresult or an exception discards the object (a freshly created object is not pinged) $maxLifetimefloat = 0.0- seconds after which an object is retired when next considered; 0 means unlimited
$idleTimeoutfloat = 0.0- seconds an object may sit idle before it is retired; 0 means unlimited
$close?callable = null- called with every object the pool discards
Throws
- \ValueError
- when
$maxis below 1
pdo()#source
static function pdo(string $dsn, ?string $user, ?string $pass, array $options, int $max): Pool
A pool of PDO connections.
release() calls PDO::resetSession() (rolling back an open transaction and clearing session state) and acquire() pings an idle connection with PDO::ping(). For $maxLifetime, $idleTimeout or a $close callback build the pool with new Pool(...). The pool holds no connection until the first acquire().
$pool = Async\Pool::pdo('pgsql:host=db;dbname=app', 'u', 'pw', [], 10);
$rows = $pool->with(fn(PDO $db) => $db->query('select 1')->fetchAll());Parameters
$dsnstring- the PDO data source name
$user?string- the database user
$pass?string- the database password
$optionsarray<int,mixed>$maxint- maximum number of connections, at least 1
Returns
Pool — a pool whose objects are PDO instances
Throws
- \ValueError
- when
$maxis below 1
acquire()#source
function acquire(?float $timeout = null): mixed
Take an object, creating one while there is capacity, else park FIFO until one is free.
The wait for a free object is bounded by $timeout; creating or validating the object is not. Pair every acquire() with release() or discard(), or use with().
Parameters
$timeout?float = null- seconds to wait for a free object, or null to wait without a limit
Returns
T — an object checked out to the caller
Throws
- TimeoutException
- when $timeout seconds pass first
- \RuntimeException
- when the pool is closed, or closed while waiting
- CancelledException
- when the task is cancelled while waiting
See also: with, release
release()#source
function release(mixed $conn): void
Return an object to the pool.
Runs the $reset hook (shielded, so it may suspend), then hands the object to the first waiter or keeps it idle. If the hook throws, the object is discarded instead. When the pool is closed the object is dropped.
Parameters
$connmixed- an object obtained from
acquire()
Throws
- \LogicException
- on a second release, or an object that is not checked out from this pool
discard()#source
function discard(mixed $conn): void
Throw an object away instead of returning it, because it is broken or its state is unknown.
The capacity it held goes to the first waiter, and the $close hook, if any, is called with the object.
Parameters
$connmixed- an object obtained from
acquire()
Throws
- \LogicException
- when the object is not checked out from this pool
with()#source
function with(callable $fn): mixed
Acquire an object, run $fn($object), and release it in a finally.
The object is discarded instead of released when the body was cancelled, or failed with a PDOException whose SQLSTATE is class 08. with() takes no timeout of its own: bound the whole call with timeout().
Parameters
$fncallable- called with the object
Returns
mixed — the value returned by $fn
Throws
- \Throwable
- whatever
$fnthrows, after the object is released or discarded
Example
use Async\Pool;
use Async\TimeoutException;
use function Async\async;
use function Async\awaitAll;
use function Async\delay;
use function Async\spawn;
final class Connection
{
public static int $made = 0;
public int $id;
public function __construct()
{
$this->id = ++self::$made;
}
}
async(function () {
// At most two connections ever exist; waiters are served in arrival order.
$pool = new Pool(fn() => new Connection(), 2);
$jobs = [];
for ($i = 1; $i <= 4; $i++) {
$jobs[] = spawn(fn() => $pool->with(function (Connection $c) {
delay(0.01);
return $c->id;
}));
}
echo implode(",", awaitAll(...$jobs)), "\n";
$stats = $pool->stats();
echo "created {$stats['created']}, idle {$stats['idle']}, busy {$stats['busy']}\n";
// Bound the wait for a free connection with a timeout.
$a = $pool->acquire();
$b = $pool->acquire();
try {
$pool->acquire(0.01);
} catch (TimeoutException $e) {
echo "no connection free\n";
}
$pool->release($a);
$pool->discard($b);
echo "discarded: {$pool->stats()['discarded']}\n";
$pool->close();
});Output
1,2,1,2
created 2, idle 2, busy 0
no connection free
discarded: 1See also: acquire
close()#source
function close(): void
Close the pool. Calling it again does nothing.
New acquire() calls are refused, every waiter wakes with a RuntimeException, and idle objects are dropped. Objects still checked out are dropped when they come back.
See also: acquire
stats()#source
function stats(): array
Counters for monitoring the pool.
Returns
array<string,int> — idle, busy, waiting, created and discarded
Example
use Async\Pool;
use Async\TimeoutException;
use function Async\async;
use function Async\awaitAll;
use function Async\delay;
use function Async\spawn;
final class Connection
{
public static int $made = 0;
public int $id;
public function __construct()
{
$this->id = ++self::$made;
}
}
async(function () {
// At most two connections ever exist; waiters are served in arrival order.
$pool = new Pool(fn() => new Connection(), 2);
$jobs = [];
for ($i = 1; $i <= 4; $i++) {
$jobs[] = spawn(fn() => $pool->with(function (Connection $c) {
delay(0.01);
return $c->id;
}));
}
echo implode(",", awaitAll(...$jobs)), "\n";
$stats = $pool->stats();
echo "created {$stats['created']}, idle {$stats['idle']}, busy {$stats['busy']}\n";
// Bound the wait for a free connection with a timeout.
$a = $pool->acquire();
$b = $pool->acquire();
try {
$pool->acquire(0.01);
} catch (TimeoutException $e) {
echo "no connection free\n";
}
$pool->release($a);
$pool->discard($b);
echo "discarded: {$pool->stats()['discarded']}\n";
$pool->close();
});Output
1,2,1,2
created 2, idle 2, busy 0
no connection free
discarded: 1