From 69273f3ff72214c4b6250d292f93b321e3c54358 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 21 May 2023 16:37:41 +0100 Subject: [PATCH] AsyncTask::setResult(): permit returning ThreadSafe objects to the main thread this is now supported thanks to the object rescue feature implemented in pthreads 5.1, making returning of thread-safe values from async tasks possible. This needs to be explicitly supported, since otherwise it will attempt to serialize them, which isn't supported anymore. --- src/scheduler/AsyncTask.php | 6 +- tests/phpunit/scheduler/AsyncPoolTest.php | 19 +++++++ .../scheduler/ThreadSafeResultAsyncTask.php | 55 +++++++++++++++++++ 3 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 tests/phpunit/scheduler/ThreadSafeResultAsyncTask.php diff --git a/src/scheduler/AsyncTask.php b/src/scheduler/AsyncTask.php index c48097bbc..367957b4e 100644 --- a/src/scheduler/AsyncTask.php +++ b/src/scheduler/AsyncTask.php @@ -25,6 +25,7 @@ namespace pocketmine\scheduler; use pmmp\thread\Runnable; use pmmp\thread\Thread as NativeThread; +use pmmp\thread\ThreadSafe; use pmmp\thread\ThreadSafeArray; use pocketmine\thread\NonThreadSafeValue; use function assert; @@ -70,8 +71,7 @@ abstract class AsyncTask extends Runnable{ /** @phpstan-var ThreadSafeArray */ public ThreadSafeArray $progressUpdates; - /** @phpstan-var NonThreadSafeValue|string|int|bool|float|null */ - private NonThreadSafeValue|string|int|bool|null|float $result = null; + private ThreadSafe|string|int|bool|null|float $result = null; private bool $cancelRun = false; private bool $submitted = false; @@ -124,7 +124,7 @@ abstract class AsyncTask extends Runnable{ } public function setResult(mixed $result) : void{ - $this->result = is_scalar($result) || is_null($result) ? $result : new NonThreadSafeValue($result); + $this->result = is_scalar($result) || is_null($result) || $result instanceof ThreadSafe ? $result : new NonThreadSafeValue($result); } public function cancelRun() : void{ diff --git a/tests/phpunit/scheduler/AsyncPoolTest.php b/tests/phpunit/scheduler/AsyncPoolTest.php index 9a61c33ff..436251d9e 100644 --- a/tests/phpunit/scheduler/AsyncPoolTest.php +++ b/tests/phpunit/scheduler/AsyncPoolTest.php @@ -24,6 +24,8 @@ declare(strict_types=1); namespace pocketmine\scheduler; use PHPUnit\Framework\TestCase; +use pmmp\thread\ThreadSafeArray; +use pocketmine\promise\PromiseResolver; use pocketmine\snooze\SleeperHandler; use pocketmine\utils\MainLogger; use function define; @@ -69,4 +71,21 @@ class AsyncPoolTest extends TestCase{ } self::assertTrue(PublishProgressRaceAsyncTask::$success, "Progress was not reported before task completion"); } + + public function testThreadSafeSetResult() : void{ + $resolver = new PromiseResolver(); + $resolver->getPromise()->onCompletion( + function(ThreadSafeArray $result) : void{ + self::assertCount(1, $result); + self::assertSame(["foo"], (array) $result); + }, + function() : void{ + self::fail("Promise failed"); + } + ); + $this->pool->submitTask(new ThreadSafeResultAsyncTask($resolver)); + while($this->pool->collectTasks()){ + usleep(50 * 1000); + } + } } diff --git a/tests/phpunit/scheduler/ThreadSafeResultAsyncTask.php b/tests/phpunit/scheduler/ThreadSafeResultAsyncTask.php new file mode 100644 index 000000000..750c56834 --- /dev/null +++ b/tests/phpunit/scheduler/ThreadSafeResultAsyncTask.php @@ -0,0 +1,55 @@ + $promise + */ + public function __construct( + PromiseResolver $promise + ){ + $this->storeLocal(self::TLS_KEY_PROMISE, $promise); + } + + public function onRun() : void{ + //this only works in pthreads 5.1+ and pmmpthread + //in prior versions the ThreadSafe would be destroyed before onCompletion is called + $result = new ThreadSafeArray(); + $result[] = "foo"; + $this->setResult($result); + } + + public function onCompletion() : void{ + /** @var PromiseResolver $promise */ + $promise = $this->fetchLocal(self::TLS_KEY_PROMISE); + $promise->resolve($this->getResult()); + } +}