Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,11 +452,15 @@ store's own shutdown function.

```php
$store = PersistentStore::boot(); // register/reattach the persistent module
$store->persist(User::class, $o): User; // convert + return canonical instance (T of class-string<T>)
$store->attach(): array; // class-string => instance for this request (idempotent)
$store->persist(User::class, $o): User; // convert + return canonical instance; the key is a NAME,
// ::class by convention so get() keeps its inference
$store->persistInstance($o): User; // per-instance graph named by its own root address:
// any number of one class live at once, none upserts another
$store->attach(): array; // name => instance for this request (idempotent)
$store->get(User::class): ?User; // canonical instance or null
$store->has(User::class): bool;
$store->drop(User::class): bool; // remove the entry + reclaim what nobody shares
$store->dropInstance($o /* or address */): bool; // same, for an instance graph
$store->objectCount(): int; // live persistent clones (shared ones counted once)
$store->detach(): void; // runs automatically at request shutdown

Expand Down
22 changes: 8 additions & 14 deletions src/Ipc/SharedError.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@
* an arena-resident string. The result slot then carries its ADDRESS, and the waiting
* process attaches the same object rather than parsing anything.
*
* ## One error entry per store, deliberately
* ## One entry per panic
*
* The object is persisted under this class as its storage key, so capturing a second error
* replaces the first (persist() is an upsert). That fits the shape of the panic path - a
* worker captures the failure that ended its task and the waiter reads it - and it keeps the
* arena from filling up with the error graphs of a crash loop. A consumer that needs several
* live panics at once should copy the three strings out of the object it attached; in arena
* mode nothing is freed while the family lives (blocks are reclaimed only at teardown), but
* a superseded object leaves the registry and can no longer be attached by address.
* Each capture is its own instance graph ({@see PersistentStore::persistInstance()}), so a
* second panic never supersedes the first: two workers failing near-simultaneously each
* leave an error a waiter can still attach by the address its own slot carries. The cost is
* three short strings per panic, held until the family tears down - the ordinary
* leak-until-teardown economics of the arena, and a panic is not a hot path.
*/
final class SharedError
{
Expand All @@ -46,10 +44,6 @@ final class SharedError
/**
* Moves a Throwable's description into the arena and returns the shared object's address
*
* Returns the address rather than the instance on purpose: holding the persistent
* instance would make the NEXT capture fail, since the store refuses to release a graph
* the request can still reach.
*
* @return int Address of the shared error-info object, for a result slot record
*/
public static function capture(PersistentStore $store, \Throwable $error): int
Expand All @@ -59,8 +53,8 @@ public static function capture(PersistentStore $store, \Throwable $error): int
$info->message = $error->getMessage();
$info->trace = $error->getTraceAsString();

$store->persist(self::class, $info);
$address = $store->addressOf(self::class);
$shared = $store->persistInstance($info);
$address = $store->addressOfInstance($shared);
\assert($address !== null);

return $address;
Expand Down
Loading
Loading