diff --git a/src/Appwrite/GraphQL/Promises/Adapter/Swoole.php b/src/Appwrite/GraphQL/Promises/Adapter/Swoole.php index bcdc28d58b..b2a53f1959 100644 --- a/src/Appwrite/GraphQL/Promises/Adapter/Swoole.php +++ b/src/Appwrite/GraphQL/Promises/Adapter/Swoole.php @@ -9,7 +9,8 @@ use GraphQL\Executor\Promise\Promise as GQLPromise; class Swoole extends Adapter { /** - * Synchronously wait for promise completion by running the task queue. + * Wait for promise completion and return the result. + * Since callbacks are executed synchronously, the promise should already be settled. * * @param GQLPromise $promise * @return mixed @@ -19,14 +20,6 @@ class Swoole extends Adapter { /** @var SwoolePromise $swoolePromise */ $swoolePromise = $promise->adoptedPromise; - $taskQueue = SwoolePromise::getQueue(); - - while ( - $swoolePromise->state === SwoolePromise::PENDING - && !$taskQueue->isEmpty() - ) { - SwoolePromise::runQueue(); - } if ($swoolePromise->state === SwoolePromise::FULFILLED) { return $swoolePromise->result; @@ -36,7 +29,8 @@ class Swoole extends Adapter throw $swoolePromise->result; } - throw new \Exception('Could not resolve promise'); + // Promise should already be settled in synchronous mode + throw new \Exception('Could not resolve promise - still pending'); } public function create(callable $resolver): GQLPromise diff --git a/src/Appwrite/Promises/Swoole.php b/src/Appwrite/Promises/Swoole.php index a3473825c6..180b06b35c 100644 --- a/src/Appwrite/Promises/Swoole.php +++ b/src/Appwrite/Promises/Swoole.php @@ -3,8 +3,8 @@ namespace Appwrite\Promises; /** - * Swoole-compatible promise implementation that uses a task queue - * for deferred callback execution, similar to graphql-php's SyncPromise. + * Swoole-compatible promise implementation that executes callbacks synchronously. + * This works with Swoole's WaitGroup pattern used in the GraphQL controller. */ class Swoole extends Promise { @@ -22,46 +22,21 @@ class Swoole extends Promise */ protected array $waiting = []; - /** - * Run all tasks in the queue - */ - public static function runQueue(): void - { - $q = self::getQueue(); - while (!$q->isEmpty()) { - $task = $q->dequeue(); - $task(); - } - } - - /** - * Get the shared task queue - * - * @return \SplQueue - */ - public static function getQueue(): \SplQueue - { - static $queue; - - return $queue ??= new \SplQueue(); - } - public function __construct(?callable $executor = null) { if ($executor === null) { return; } - self::getQueue()->enqueue(function () use ($executor): void { - try { - $executor( - fn ($value) => $this->resolve($value), - fn ($reason) => $this->reject($reason) - ); - } catch (\Throwable $e) { - $this->reject($e); - } - }); + // Execute the executor synchronously + try { + $executor( + fn ($value) => $this->resolve($value), + fn ($reason) => $this->reject($reason) + ); + } catch (\Throwable $e) { + $this->reject($e); + } } protected function execute( @@ -69,7 +44,7 @@ class Swoole extends Promise callable $resolve, callable $reject ): void { - // Not used - we use the task queue mechanism instead + // Not used - we execute synchronously in constructor } /** @@ -92,7 +67,7 @@ class Swoole extends Promise $this->state = self::FULFILLED; $this->result = $value; - $this->enqueueWaitingPromises(); + $this->processWaiting(); return $this; } @@ -108,36 +83,34 @@ class Swoole extends Promise $this->state = self::REJECTED; $this->result = $reason; - $this->enqueueWaitingPromises(); + $this->processWaiting(); return $this; } /** - * Enqueue callbacks for waiting promises + * Process waiting callbacks immediately (synchronously) */ - protected function enqueueWaitingPromises(): void + protected function processWaiting(): void { foreach ($this->waiting as [$promise, $onFulfilled, $onRejected]) { - self::getQueue()->enqueue(function () use ($promise, $onFulfilled, $onRejected): void { - if ($this->state === self::FULFILLED) { - try { - $promise->resolve($onFulfilled === null ? $this->result : $onFulfilled($this->result)); - } catch (\Throwable $e) { - $promise->reject($e); - } - } elseif ($this->state === self::REJECTED) { - try { - if ($onRejected === null) { - $promise->reject($this->result); - } else { - $promise->resolve($onRejected($this->result)); - } - } catch (\Throwable $e) { - $promise->reject($e); - } + if ($this->state === self::FULFILLED) { + try { + $promise->resolve($onFulfilled === null ? $this->result : $onFulfilled($this->result)); + } catch (\Throwable $e) { + $promise->reject($e); } - }); + } elseif ($this->state === self::REJECTED) { + try { + if ($onRejected === null) { + $promise->reject($this->result); + } else { + $promise->resolve($onRejected($this->result)); + } + } catch (\Throwable $e) { + $promise->reject($e); + } + } } $this->waiting = []; @@ -156,10 +129,29 @@ class Swoole extends Promise } $promise = new self(); - $this->waiting[] = [$promise, $onFulfilled, $onRejected]; - if ($this->state !== self::PENDING) { - $this->enqueueWaitingPromises(); + if ($this->state === self::PENDING) { + // Promise not settled yet - queue the callbacks + $this->waiting[] = [$promise, $onFulfilled, $onRejected]; + } else { + // Promise already settled - execute callback immediately + if ($this->state === self::FULFILLED) { + try { + $promise->resolve($onFulfilled === null ? $this->result : $onFulfilled($this->result)); + } catch (\Throwable $e) { + $promise->reject($e); + } + } else { + try { + if ($onRejected === null) { + $promise->reject($this->result); + } else { + $promise->resolve($onRejected($this->result)); + } + } catch (\Throwable $e) { + $promise->reject($e); + } + } } return $promise; @@ -201,9 +193,10 @@ class Swoole extends Promise $count = 0; $result = []; + $rejected = false; - $resolveAllWhenFinished = static function () use (&$count, $total, $all, &$result): void { - if ($count === $total) { + $resolveAllWhenFinished = static function () use (&$count, $total, $all, &$result, &$rejected): void { + if (!$rejected && $count === $total) { $all->resolve($result); } }; @@ -217,7 +210,12 @@ class Swoole extends Promise ++$count; $resolveAllWhenFinished(); }, - [$all, 'reject'] + static function ($error) use (&$rejected, $all) { + if (!$rejected) { + $rejected = true; + $all->reject($error); + } + } ); } else { $result[$index] = $promiseOrValue; @@ -229,4 +227,18 @@ class Swoole extends Promise return $all; } + + /** + * Static queue methods for graphql-php compatibility (not used in sync mode) + */ + public static function runQueue(): void + { + // No-op in synchronous mode + } + + public static function getQueue(): \SplQueue + { + static $queue; + return $queue ??= new \SplQueue(); + } }