diff --git a/src/Context/Context.php b/src/Context/Context.php index 9dcd268..5ad285b 100644 --- a/src/Context/Context.php +++ b/src/Context/Context.php @@ -16,8 +16,7 @@ interface Context extends Channel /** * @return TResult The data returned from the context. * - * @throws ContextException If the context dies unexpectedly. - * @throws ContextPanicError If the context throws an uncaught exception. + * @throws ContextException If the context exited with an uncaught exception or non-zero code. */ public function join(?Cancellation $cancellation = null): mixed; } diff --git a/src/Context/Internal/AbstractContext.php b/src/Context/Internal/AbstractContext.php index 2ec43e4..040ce83 100644 --- a/src/Context/Internal/AbstractContext.php +++ b/src/Context/Internal/AbstractContext.php @@ -36,34 +36,33 @@ abstract class AbstractContext implements Context } catch (ChannelException $exception) { try { $data = $this->join(new TimeoutCancellation(0.1)); - } catch (ContextException|ChannelException|CancelledException) { + } catch (ChannelException|CancelledException) { if (!$this->isClosed()) { $this->close(); } throw new ContextException( - "The process stopped responding, potentially due to a fatal error or calling exit", - 0, - $exception, + "The context stopped responding, potentially due to a fatal error or calling exit", + previous: $exception, ); } - throw new \Error(\sprintf( - 'Process unexpectedly exited when waiting to receive data with result: %s', + throw new ContextException(\sprintf( + 'Context unexpectedly exited when waiting to receive data with result: %s', flattenArgument($data), - ), 0, $exception); + ), previous: $exception); } if (!$data instanceof ContextMessage) { if ($data instanceof ExitResult) { $data = $data->getResult(); - throw new \Error(\sprintf( - 'Process unexpectedly exited when waiting to receive data with result: %s', + throw new ContextException(\sprintf( + 'Context unexpectedly exited when waiting to receive data with result: %s', flattenArgument($data), )); } - throw new \Error(\sprintf( + throw new ContextException(\sprintf( 'Unexpected data type from context: %s', flattenArgument($data), )); @@ -79,19 +78,19 @@ abstract class AbstractContext implements Context } catch (ChannelException $exception) { try { $data = $this->join(new TimeoutCancellation(0.1)); - } catch (ContextException|ChannelException|CancelledException) { + } catch (ChannelException|CancelledException) { if (!$this->isClosed()) { $this->close(); } + throw new ContextException( - "The process stopped responding, potentially due to a fatal error or calling exit", - 0, - $exception, + "The context stopped responding, potentially due to a fatal error or calling exit", + previous: $exception, ); } - throw new \Error(\sprintf( - 'Process unexpectedly exited when sending data with result: %s', + throw new ContextException(\sprintf( + 'Context unexpectedly exited when sending data with result: %s', flattenArgument($data), ), 0, $exception); } @@ -115,7 +114,7 @@ abstract class AbstractContext implements Context protected function receiveExitResult(?Cancellation $cancellation = null): ExitResult { if ($this->channel->isClosed()) { - throw new ContextException("The context has already closed"); + throw new ContextException("The context has already closed without providing a result"); } try { @@ -126,7 +125,7 @@ abstract class AbstractContext implements Context if (!$this->isClosed()) { $this->close(); } - throw new ContextException("Failed to receive result from context", 0, $exception); + throw new ContextException("Failed to receive result from context", previous: $exception); } if (!$data instanceof ExitResult) { @@ -135,13 +134,13 @@ abstract class AbstractContext implements Context } if ($data instanceof ContextMessage) { - throw new \Error(\sprintf( - "The context sent data instead of exiting: %s", - flattenArgument($data), - )); + $data = $data->getMessage(); } - throw new \Error("Did not receive an exit result from context"); + throw new ContextException(\sprintf( + "The context sent data instead of exiting: %s", + flattenArgument($data), + )); } $this->channel->close(); diff --git a/src/Context/Internal/ExitFailure.php b/src/Context/Internal/ExitFailure.php index 5572f04..3ab404e 100644 --- a/src/Context/Internal/ExitFailure.php +++ b/src/Context/Internal/ExitFailure.php @@ -2,6 +2,7 @@ namespace Amp\Parallel\Context\Internal; +use Amp\Parallel\Context\ContextException; use Amp\Parallel\Context\ContextPanicError; use function Amp\Parallel\Context\flattenThrowableBacktrace; @@ -42,11 +43,16 @@ final class ExitFailure implements ExitResult } /** - * @throws ContextPanicError + * @throws ContextException */ public function getResult(): never { - throw $this->createException(); + $exception = $this->createException(); + + throw new ContextException( + 'Process exited with an uncaught exception: ' . $exception->getMessage(), + previous: $exception, + ); } private function createException(): ContextPanicError diff --git a/src/Context/Internal/ExitResult.php b/src/Context/Internal/ExitResult.php index c4792ac..7a46585 100644 --- a/src/Context/Internal/ExitResult.php +++ b/src/Context/Internal/ExitResult.php @@ -2,7 +2,7 @@ namespace Amp\Parallel\Context\Internal; -use Amp\Parallel\Context\ContextPanicError; +use Amp\Parallel\Context\ContextException; /** * @internal @@ -13,7 +13,7 @@ interface ExitResult /** * @return TValue Return value of the callable given to the execution context. * - * @throws ContextPanicError If the context exited with an uncaught exception. + * @throws ContextException If the context exited with an uncaught exception. */ public function getResult(): mixed; } diff --git a/test/Context/AbstractContextTest.php b/test/Context/AbstractContextTest.php index 374ad4c..c1dd941 100644 --- a/test/Context/AbstractContextTest.php +++ b/test/Context/AbstractContextTest.php @@ -5,7 +5,6 @@ namespace Amp\Parallel\Test\Context; use Amp\CancelledException; use Amp\Parallel\Context\Context; use Amp\Parallel\Context\ContextException; -use Amp\Parallel\Context\ContextPanicError; use Amp\PHPUnit\AsyncTestCase; use Amp\TimeoutCancellation; use function Amp\async; @@ -26,7 +25,7 @@ abstract class AbstractContextTest extends AsyncTestCase public function testFailingProcess(): void { - $this->expectException(ContextPanicError::class); + $this->expectException(ContextException::class); $this->expectExceptionMessage('No string provided'); $context = $this->createContext(__DIR__ . "/Fixtures/test-process.php"); @@ -35,40 +34,33 @@ abstract class AbstractContextTest extends AsyncTestCase public function testThrowingProcessOnReceive(): void { - $this->expectException(ContextPanicError::class); + $this->expectException(ContextException::class); $this->expectExceptionMessage('Test message'); $context = $this->createContext(__DIR__ . "/Fixtures/throwing-process.php"); $cancellation = new TimeoutCancellation(0.1); - try { - $context->receive($cancellation); - self::fail('Receiving should have failed'); - } catch (ContextException) { - $context->join($cancellation); - } + $context->receive($cancellation); + + self::fail('Receiving should have failed'); } public function testThrowingProcessOnSend(): void { - $this->expectException(ContextPanicError::class); - $this->expectExceptionMessage('Test message'); + $this->expectException(ContextException::class); $context = $this->createContext(__DIR__ . "/Fixtures/throwing-process.php"); - delay(1); + delay(1); // Give process time to start. - try { - $context->send(1); - self::fail('Sending should have failed'); - } catch (ContextException) { - $context->join(new TimeoutCancellation(1)); - } + $context->send(1); + + self::fail('Sending should have failed'); } public function testInvalidScriptPath(): void { - $this->expectException(ContextPanicError::class); + $this->expectException(ContextException::class); $this->expectExceptionMessage("No script found at '../test-process.php'"); $context = $this->createContext("../test-process.php"); @@ -77,7 +69,7 @@ abstract class AbstractContextTest extends AsyncTestCase public function testInvalidResult(): void { - $this->expectException(ContextPanicError::class); + $this->expectException(ContextException::class); $this->expectExceptionMessage('The given data could not be serialized'); $context = $this->createContext(__DIR__ . "/Fixtures/invalid-result-process.php"); @@ -86,7 +78,7 @@ abstract class AbstractContextTest extends AsyncTestCase public function testNoCallbackReturned(): void { - $this->expectException(ContextPanicError::class); + $this->expectException(ContextException::class); $this->expectExceptionMessage('did not return a callable function'); $context = $this->createContext(__DIR__ . "/Fixtures/no-callback-process.php"); @@ -95,7 +87,7 @@ abstract class AbstractContextTest extends AsyncTestCase public function testParseError(): void { - $this->expectException(ContextPanicError::class); + $this->expectException(ContextException::class); $this->expectExceptionMessage('contains a parse error'); $context = $this->createContext(__DIR__ . "/Fixtures/parse-error-process.inc"); diff --git a/test/Context/Internal/ExitFailureTest.php b/test/Context/Internal/ExitFailureTest.php index 9bc0d9d..7385286 100644 --- a/test/Context/Internal/ExitFailureTest.php +++ b/test/Context/Internal/ExitFailureTest.php @@ -2,7 +2,7 @@ namespace Amp\Parallel\Test\Context\Internal; -use Amp\Parallel\Context\ContextPanicError; +use Amp\Parallel\Context\ContextException; use Amp\Parallel\Context\Internal\ExitFailure; use Amp\PHPUnit\AsyncTestCase; @@ -15,7 +15,7 @@ class ExitFailureTest extends AsyncTestCase $result = new ExitFailure($exception); try { $result->getResult(); - } catch (ContextPanicError $caught) { + } catch (ContextException $caught) { self::assertGreaterThan(0, \stripos($caught->getMessage(), $message)); return; }