The graphql-php library uses its own SyncPromise queue for Deferred
resolution, which is separate from our SwoolePromise queue. The wait()
method now runs both queues to ensure all deferred tasks are processed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace WaitGroup-based promise waiting with adapter's wait() method
which properly runs the deferred task queue. The previous implementation
would hang indefinitely because the queue was never processed, causing
the WaitGroup callback to never be called.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Callbacks are now enqueued to task queue instead of executed immediately
- wait() runs the queue until promise settles (like SyncPromiseAdapter)
- This properly supports graphql-php's deferred execution model
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Call through GQLPromise::then() instead of directly on adopted promise.
This matches how SyncPromiseAdapter handles promise chaining.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The GraphQL controller uses Swoole WaitGroup to wait for promise callbacks.
Queue-based deferred execution causes deadlock because runQueue() is never
called. This change executes callbacks immediately when then() is called
on settled promises, allowing the WaitGroup pattern to work correctly.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Call resolver synchronously instead of enqueuing it.
This matches how graphql-php's SyncPromiseAdapter works.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add task queue mechanism to SwoolePromise matching SyncPromise behavior
- Callbacks are enqueued instead of executed immediately
- Add wait() method to Swoole adapter to process queue until promise settles
- This fixes hanging tests by properly executing deferred promise callbacks
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove all coroutine-based execution. The executor now runs
synchronously in the constructor, matching graphql-php's SyncPromise
behavior. This ensures all promise operations complete immediately
without scheduling issues.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Call then() on the adopted SwoolePromise directly instead of going
through the GQLPromise wrapper. This ensures callbacks are properly
registered and fired via SwoolePromise's processWaiting().
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
These methods should return already-resolved promises, not promises
that will resolve in a future coroutine. This fixes the issue where
graphql-php's synchronous execution returns "fulfilled" promises that
are actually still pending.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of relying on then() callbacks which may not fire correctly,
poll the adopted promise state directly using Swoole Coroutine::sleep
for proper yielding. Each promise is waited on in its own coroutine.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This avoids scheduling issues where spawned coroutines don't get a
chance to run before the main coroutine continues waiting.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace busy-waiting in then() with proper callback queuing:
- Store waiting callbacks in array when promise is pending
- When promise resolves/rejects, process all waiting callbacks
- Run callbacks in coroutines for proper async execution
- This eliminates deadlocks from busy-wait polling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add public resolve() and reject() methods to Promise class
- Create combined promise without executor (no coroutine spawned)
- Use direct resolve/reject calls instead of captured callbacks
- This ensures the promise can be settled regardless of coroutine timing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use callback-based approach similar to graphql-php's SyncPromiseAdapter:
- Create a combined promise and capture resolve/reject callbacks
- Register then() callbacks on each input promise
- Resolve when all callbacks have fired
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use Swoole Coroutine::sleep for proper coroutine yielding instead of
callback-based completion tracking
- Spawn a coroutine for each promise to wait for its completion
- Use Channel for synchronization between coroutines
- Add public accessors to Promise class for state checking
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use callback-based counting instead of channel synchronization to track
promise completion. Work directly with GQLPromise objects and use their
then() method which properly chains through the adapter to the underlying
SwoolePromise.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of reimplementing the promise aggregation logic, extract adopted
promises from GQLPromise wrappers and delegate to SwoolePromise::all()
which already handles the coroutine synchronization properly.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of extracting adopted promises and delegating to SwoolePromise::all(),
implement the batch handling logic directly to properly work with GQLPromise
wrappers. Uses Swoole Channel for synchronization.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The graphql-php executor passes both promises and plain values to
the PromiseAdapter::all() method. Updated SwoolePromise::all() to
properly handle plain values by storing them directly without
attempting to call ->then() on them.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>