Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
319f8d9
Preserve cancellation precedence across chained failures
binaryfire Nov 2, 2026
63a5a34
Keep cancellation out of logger serialization fallbacks
binaryfire Nov 2, 2026
71767b7
Preserve cancellation through safe-call reporting
binaryfire Nov 2, 2026
1376b5f
Propagate cancellation from string replacements
binaryfire Nov 2, 2026
5ec7de0
Avoid padding canceled timeboxed operations
binaryfire Nov 2, 2026
9b26c0e
Preserve cancellation while restoring cached objects
binaryfire Nov 2, 2026
ca5b5ed
Preserve cancellation when releasing cache locks
binaryfire Nov 2, 2026
7a6b3b5
Preserve cancellation when releasing cache concurrency leases
binaryfire Nov 2, 2026
28baacb
Exclude wrapped cancellation from cache failure events
binaryfire Nov 2, 2026
9f9679c
Stop tagged cache work when cancellation is wrapped
binaryfire Nov 2, 2026
03d915f
Preserve cancellation in Redis tagged cache operations
binaryfire Nov 2, 2026
b3efaa0
Stop user cache invalidation on cancellation
binaryfire Nov 2, 2026
1fa4030
Preserve cancellation during Passport token issuance cleanup
binaryfire Nov 2, 2026
dded19b
Stop Passport cache invalidation on wrapped cancellation
binaryfire Nov 2, 2026
b68bbaf
Stop feature-cache rotation on wrapped cancellation
binaryfire Nov 2, 2026
99a67cf
Add a cancellation-aware PSR stream resource bridge
binaryfire Nov 2, 2026
e27e683
Preserve cancellation through HTTP response resources
binaryfire Nov 2, 2026
97d9c40
Preserve filesystem cancellation and stream ownership
binaryfire Nov 2, 2026
61b8bd2
Stop read-through promotion after cancellation
binaryfire Nov 2, 2026
3198de7
Preserve cancellation and decoded-body ownership in S3 reads
binaryfire Nov 2, 2026
85796f7
Preserve cancellation and decoded-body ownership in GCS reads
binaryfire Nov 2, 2026
9c507dc
Preserve cancellation precedence during pooled database release
binaryfire Nov 2, 2026
1c43b90
Preserve wrapped cancellation across Redis operations and cleanup
binaryfire Nov 2, 2026
4ebd70d
Preserve cancellation from Redis concurrency lease cleanup
binaryfire Nov 2, 2026
b597b4d
Preserve cancellation during process and pool cleanup
binaryfire Nov 2, 2026
d5d411c
Preserve cancellation in batch callbacks and option restoration
binaryfire Nov 2, 2026
13d7238
Clean up canceled batch dispatch without losing its cause
binaryfire Nov 2, 2026
0662741
Preserve cancellation while releasing debounce state
binaryfire Nov 2, 2026
1bf97fd
Preserve cancellation while releasing dispatch locks
binaryfire Nov 2, 2026
79e342a
Keep notification cancellation through failure observers
binaryfire Nov 2, 2026
fe9e677
Stop OAuth account renewal retries after cancellation
binaryfire Nov 2, 2026
0bddc8d
Preserve session deletion cancellation through cleanup errors
binaryfire Nov 2, 2026
65fd368
Propagate cancellation from optional social email lookups
binaryfire Nov 2, 2026
a0ea12d
Preserve cancellation during compromised-password checks
binaryfire Nov 2, 2026
e4b7b27
Preserve cancellation through view rendering observers
binaryfire Nov 2, 2026
07a645d
Merge branch '0.4' into fix/cancellation-propagation
binaryfire Nov 2, 2026
78298c9
Preserve decorated stream ownership during resource casting
binaryfire Nov 2, 2026
768493c
Inspect both Google exception causes for cancellation
binaryfire Nov 2, 2026
c49010f
Propagate cancellation from file lock owner deserialization
binaryfire Nov 2, 2026
9995a29
Finalize cache serialization policy in custom server processes
binaryfire Nov 2, 2026
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
1 change: 1 addition & 0 deletions src/auth/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"hypervel/context": "^0.4",
"hypervel/contracts": "^0.4",
"hypervel/core": "^0.4",
"hypervel/coroutine": "^0.4",
"hypervel/database": "^0.4",
"hypervel/hashing": "^0.4",
"hypervel/http": "^0.4",
Expand Down
7 changes: 6 additions & 1 deletion src/auth/src/EloquentUserProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Hypervel\Contracts\Cache\Repository as CacheRepository;
use Hypervel\Contracts\Hashing\Hasher as HasherContract;
use Hypervel\Contracts\Support\Arrayable;
use Hypervel\Coroutine\Coroutine;
use Hypervel\Database\Eloquent\Builder;
use Hypervel\Database\Eloquent\Model;
use InvalidArgumentException;
Expand Down Expand Up @@ -562,14 +563,18 @@ protected function registerCacheInvalidationEvents(): void
$coordinator = $container->make(ModelCacheCoordinator::class);
$exception = null;

// Every configured store must be attempted even if another store is unavailable.
// Ordinary store failures must not prevent invalidation of the remaining stores.
foreach (static::$cachedProviders[$modelClass] ?? [] as $descriptor) {
try {
$coordinator->invalidate(
$cacheManager->store($descriptor['storeName']),
$descriptor['prefix'] . ':' . $modelClass . ':' . $identifierSegment,
);
} catch (Throwable $throwable) {
if (Coroutine::causedByCancellation($throwable)) {
throw $throwable;
}

$exception ??= $throwable;
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/bus/src/Batch.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Hypervel\Contracts\Events\Dispatcher;
use Hypervel\Contracts\Queue\Factory as QueueFactory;
use Hypervel\Contracts\Support\Arrayable;
use Hypervel\Coroutine\Coroutine;
use Hypervel\Database\ConnectionResolverInterface;
use Hypervel\Queue\CallQueuedClosure;
use Hypervel\Support\Arr;
Expand Down Expand Up @@ -408,6 +409,10 @@ protected function invokeHandlerCallback(callable $handler, Batch $batch, ?Throw
try {
$handler($batch, $e);
} catch (Throwable $e) {
if (Coroutine::causedByCancellation($e)) {
throw $e;
}

if (function_exists('report')) {
report($e);
}
Expand Down
7 changes: 6 additions & 1 deletion src/bus/src/DatabaseBatchRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Closure;
use DateTimeInterface;
use Hypervel\Coroutine\Coroutine;
use Hypervel\Database\ConnectionInterface;
use Hypervel\Database\ConnectionResolverInterface;
use Hypervel\Database\PostgresConnection;
Expand Down Expand Up @@ -289,7 +290,11 @@ protected function unserialize(string $serialized): mixed

try {
return unserialize($serialized);
} catch (Throwable) {
} catch (Throwable $exception) {
if (Coroutine::causedByCancellation($exception)) {
throw $exception;
}

return [];
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/bus/src/DebounceLock.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Hypervel\Bus;

use Hypervel\Contracts\Cache\Repository as Cache;
use Hypervel\Coroutine\Coroutine;
use Hypervel\Queue\Attributes\DebounceFor;
use Hypervel\Queue\Attributes\ReadsQueueAttributes;
use Hypervel\Support\CarbonImmutable;
Expand Down Expand Up @@ -89,8 +90,8 @@ protected function acquireResolvedLock(Cache $cache, string $key, int $ttl, ?int
} catch (Throwable $exception) {
try {
static::releaseOwned($cache, $key, $owner);
} catch (Throwable) {
// A cleanup failure must not replace the acquisition failure.
} catch (Throwable $cleanupException) {
throw Coroutine::preferCancellation($exception, $cleanupException);
}

throw $exception;
Expand Down Expand Up @@ -182,7 +183,7 @@ public static function releaseOwned(Cache $cache, string $key, string $owner = '
try {
$cache->forget($key . ':first_dispatched_at');
} catch (Throwable $throwable) {
$exception ??= $throwable;
$exception = Coroutine::preferCancellation($exception, $throwable);
}

if ($exception !== null) {
Expand Down
3 changes: 2 additions & 1 deletion src/bus/src/DispatchLockContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Closure;
use Hypervel\Contracts\Cache\Repository as Cache;
use Hypervel\Coroutine\Coroutine;
use Throwable;
use WeakMap;

Expand Down Expand Up @@ -311,7 +312,7 @@ protected static function releaseLocks(array $snapshot): void
);
}
} catch (Throwable $throwable) {
$exception ??= $throwable;
$exception = Coroutine::preferCancellation($exception, $throwable);
}

if ($exception !== null) {
Expand Down
47 changes: 33 additions & 14 deletions src/bus/src/PendingBatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -347,11 +347,14 @@ public function dispatch(): Batch

$batch = $batch->add($this->jobs);
} catch (Throwable $e) {
if (isset($batch)) {
$repository->delete($batch->id);
// Keep the original failure in flight so cleanup errors retain its cancellation chain.
try {
throw $e;
} finally {
if (isset($batch)) {
$repository->delete($batch->id);
}
}

throw $e;
}

$events = $this->container->make(EventDispatcher::class);
Expand Down Expand Up @@ -387,9 +390,12 @@ protected function dispatchExistingBatch(Batch $batch): void
try {
$batch = $batch->add($this->jobs);
} catch (Throwable $e) {
$batch->delete();

throw $e;
// Keep the original failure in flight so cleanup errors retain its cancellation chain.
try {
throw $e;
} finally {
$batch->delete();
}
}

$events = $this->container->make(EventDispatcher::class);
Expand Down Expand Up @@ -422,15 +428,28 @@ protected function store(BatchRepository $repository): Batch
{
$batch = $repository->store($this);

(new Collection($this->beforeCallbacks()))->each(function ($handler) use ($batch) {
try {
return $handler($batch);
} catch (Throwable $e) {
if (function_exists('report')) {
report($e);
try {
(new Collection($this->beforeCallbacks()))->each(function ($handler) use ($batch) {
try {
return $handler($batch);
} catch (Throwable $e) {
if (Coroutine::causedByCancellation($e)) {
throw $e;
}

if (function_exists('report')) {
report($e);
}
}
});
} catch (Throwable $e) {
// Dispatch cannot own cleanup until store returns the saved batch.
try {
throw $e;
} finally {
$repository->delete($batch->id);
}
});
}

return $batch;
}
Expand Down
10 changes: 3 additions & 7 deletions src/cache/src/AnyModeTaggedCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use Hypervel\Cache\Events\KeyForgotten;
use Hypervel\Cache\Events\KeyRetrievalFailed;
use Hypervel\Cache\Events\RetrievingKey;
use Swoole\Coroutine\CanceledException;
use Hypervel\Coroutine\Coroutine;
use Throwable;
use UnitEnum;

Expand Down Expand Up @@ -160,10 +160,8 @@ protected function getPlainRaw(UnitEnum|string $key): mixed

try {
$value = $this->handleIncompleteClass($key, $this->store->get($key));
} catch (CanceledException $exception) {
throw $exception;
} catch (Throwable $exception) {
if ($this->events?->hasListeners(KeyRetrievalFailed::class)) {
if (! Coroutine::causedByCancellation($exception) && $this->events?->hasListeners(KeyRetrievalFailed::class)) {
$this->event(new KeyRetrievalFailed($this->getName(), $key, $exception));
}

Expand Down Expand Up @@ -202,10 +200,8 @@ protected function forgetPlainKey(UnitEnum|string $key): bool

try {
$result = $this->store->forget($key);
} catch (CanceledException $exception) {
throw $exception;
} catch (Throwable $exception) {
if ($this->events?->hasListeners(KeyForgetFailed::class)) {
if (! Coroutine::causedByCancellation($exception) && $this->events?->hasListeners(KeyForgetFailed::class)) {
$this->event(new KeyForgetFailed($this->getName(), $key, exception: $exception));
}

Expand Down
7 changes: 4 additions & 3 deletions src/cache/src/CacheManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -596,9 +596,10 @@ public function setApplication(Container $app): static
* Register classes that cache stores may unserialize.
*
* Boot-only. The resolver contributes to the worker-lifetime cache policy
* and is evaluated at application boot completion in console processes or
* after the worker configuration is rebuilt in Swoole workers. An earlier
* cache read evaluates the current contributions without memoizing them.
* and is evaluated when the application finishes booting in console processes,
* after configuration reload in Swoole workers, or before a custom server
* process starts handling work. An earlier cache read evaluates the current
* contributions without memoizing them.
*
* @param Closure(): array<array-key, class-string> $resolver
*
Expand Down
5 changes: 3 additions & 2 deletions src/cache/src/CacheServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Hypervel\Cache\Redis\Console\DoctorCommand;
use Hypervel\Core\Events\AfterWorkerStart;
use Hypervel\Core\Events\BeforeServerStart;
use Hypervel\ServerProcess\Events\BeforeProcessHandle;
use Hypervel\Support\ServiceProvider;

class CacheServiceProvider extends ServiceProvider
Expand Down Expand Up @@ -64,8 +65,8 @@ public function boot(): void
return;
}

// Finalize after BeforeWorkerStart rebuilds the worker configuration.
$events->listen(AfterWorkerStart::class, function (AfterWorkerStart $event): void {
// Workers finalize after configuration reload; custom processes inherit the master's configuration.
$events->listen([AfterWorkerStart::class, BeforeProcessHandle::class], function (): void {
$this->app->make(CacheManager::class)->finalizeSerializableClasses();
});
}
Expand Down
17 changes: 11 additions & 6 deletions src/cache/src/FileStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Hypervel\Contracts\Cache\LockProvider;
use Hypervel\Contracts\Cache\Store;
use Hypervel\Contracts\Filesystem\LockTimeoutException;
use Hypervel\Coroutine\Coroutine;
use Hypervel\Filesystem\Filesystem;
use Hypervel\Filesystem\LockableFile;
use Hypervel\Support\InteractsWithTime;
Expand Down Expand Up @@ -192,9 +193,11 @@ public function getLockOwner(string $key): ?string
}

return $this->unserialize(substr($contents, 10));
} catch (CanceledException $exception) {
throw $exception;
} catch (Exception) {
} catch (Exception $exception) {
if (Coroutine::causedByCancellation($exception)) {
throw $exception;
}

return null;
}
}
Expand Down Expand Up @@ -540,9 +543,11 @@ protected function getPayload(string $key): array

try {
$data = $this->unserialize(substr($contents, 10));
} catch (CanceledException $exception) {
throw $exception;
} catch (Exception) {
} catch (Exception $exception) {
if (Coroutine::causedByCancellation($exception)) {
throw $exception;
}

$this->forget($key);

return $this->emptyPayload();
Expand Down
10 changes: 3 additions & 7 deletions src/cache/src/Limiters/ConcurrencyLimiter.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
use Hypervel\Contracts\Cache\RefreshableLock;
use Hypervel\Contracts\Limiters\Lease;
use Hypervel\Contracts\Limiters\LimiterTimeoutException;
use Hypervel\Coroutine\Coroutine;
use Hypervel\Database\ConnectionResolver;
use Hypervel\Support\Sleep;
use Hypervel\Support\Str;
use Swoole\Coroutine\CanceledException;
use Throwable;

use function Hypervel\Support\now;
Expand Down Expand Up @@ -93,12 +93,8 @@ public function block(int $timeout, ?callable $callback = null, int $sleep = 250
} catch (Throwable $throwable) {
try {
$lease->release();
} catch (CanceledException $cleanupCancellation) {
if (! $throwable instanceof CanceledException) {
throw $cleanupCancellation;
}
} catch (Throwable) {
// Preserve the callback failure as primary.
} catch (Throwable $cleanupFailure) {
throw Coroutine::preferCancellation($throwable, $cleanupFailure);
}

throw $throwable;
Expand Down
10 changes: 3 additions & 7 deletions src/cache/src/Limiters/ConcurrencyLimiterBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
use Hypervel\Contracts\Cache\Store;
use Hypervel\Contracts\Limiters\Lease;
use Hypervel\Contracts\Limiters\LimiterTimeoutException;
use Hypervel\Coroutine\Coroutine;
use Hypervel\Support\InteractsWithTime;
use Swoole\Coroutine\CanceledException;
use Throwable;

class ConcurrencyLimiterBuilder
Expand Down Expand Up @@ -124,12 +124,8 @@ public function then(callable $callback, ?callable $failure = null): mixed
} catch (Throwable $throwable) {
try {
$lease->release();
} catch (CanceledException $cleanupCancellation) {
if (! $throwable instanceof CanceledException) {
throw $cleanupCancellation;
}
} catch (Throwable) {
// Preserve the callback failure as primary.
} catch (Throwable $cleanupFailure) {
throw Coroutine::preferCancellation($throwable, $cleanupFailure);
}

throw $throwable;
Expand Down
10 changes: 3 additions & 7 deletions src/cache/src/Lock.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

use Hypervel\Contracts\Cache\Lock as LockContract;
use Hypervel\Contracts\Cache\LockTimeoutException;
use Hypervel\Coroutine\Coroutine;
use Hypervel\Database\ConnectionResolver;
use Hypervel\Support\CarbonImmutable;
use Hypervel\Support\InteractsWithTime;
use Hypervel\Support\Sleep;
use Hypervel\Support\Str;
use RuntimeException;
use Swoole\Coroutine\CanceledException;
use Throwable;

abstract class Lock implements LockContract
Expand Down Expand Up @@ -131,12 +131,8 @@ protected function releaseAfterFailure(Throwable $failure): never
{
try {
$this->release();
} catch (CanceledException $cleanupCancellation) {
if (! $failure instanceof CanceledException) {
throw $cleanupCancellation;
}
} catch (Throwable) {
// Preserve the callback failure as primary.
} catch (Throwable $cleanupFailure) {
throw Coroutine::preferCancellation($failure, $cleanupFailure);
}

throw $failure;
Expand Down
Loading
Loading