/** * Start the coroutine. * * @param StrandInterface $strand The strand that is executing the coroutine. */ public function call(StrandInterface $strand) { $this->strand = $strand; $this->strand->suspend(); foreach ($this->coroutines as $index => $coroutine) { $this->values[$index] = null; $this->substrands[$index] = $substrand = $strand->kernel()->execute($coroutine); $substrand->on('success', function ($strand, $value) use($index) { $this->values[$index] = $value; }); $substrand->on('error', function ($strand, $exception, $preventDefault) { $this->exception = $exception; $preventDefault(); }); $substrand->on('terminate', function () { $this->exception = new StrandTerminatedException(); }); $substrand->on('exit', function () use($index) { unset($this->substrands[$index]); if (!$this->strand) { return; } elseif ($this->exception) { $this->strand->resumeWithException($this->exception); $this->strand = null; } elseif (!$this->substrands) { $this->strand->resumeWithValue($this->values); $this->strand = null; } }); } }
/** * Invoked when tick() is called for the first time. * * @param StrandInterface $strand The strand that is executing the coroutine. */ public function call(StrandInterface $strand) { $strand->suspend(); $this->timer = $strand->kernel()->eventLoop()->addTimer($this->timeout, function () use($strand) { $strand->resumeWithValue(null); }); }
/** * Inform the coroutine that the executing strand is being terminated. * * @param StrandInterface $strand The strand that is executing the coroutine. */ public function terminate(StrandInterface $strand) { $strand->emit('terminate', [$strand]); $strand->emit('exit', [$strand]); $strand->removeAllListeners(); $strand->suspend(); }
/** * Inform the coroutine that the executing strand is being terminated. * * @param StrandInterface $strand The strand that is executing the coroutine. */ public function terminate(StrandInterface $strand) { if (!$this->timer) { // Stop termination of the strand and instead propagate a timeout exception. $strand->throwException(new TimeoutException()); } }
/** * Start the coroutine. * * @param StrandInterface $strand The strand that is executing the coroutine. */ public function call(StrandInterface $strand) { $strand->suspend(); $this->promise->then(function ($value) use($strand) { if ($this->promise) { $strand->resumeWithValue($value); } }, function ($reason) use($strand) { if ($this->promise) { $strand->resumeWithException($this->adaptReasonToException($reason)); } }); }
/** * Resume execution of a suspended coroutine by passing it an exception. * * @param StrandInterface $strand The strand that is executing the coroutine. * @param Exception $exception The exception to send to the coroutine. */ public function resumeWithException(StrandInterface $strand, Exception $exception) { try { $this->generator->throw($exception); $valid = $this->generator->valid(); } catch (Exception $e) { $strand->throwException($e); return; } if ($valid) { $strand->call($this->generator->current()); } elseif (self::$hasReturnValue) { $strand->returnValue($this->generator->getReturn()); } else { $strand->returnValue(null); } }
/** * Start the coroutine. * * @param StrandInterface $strand The strand that is executing the coroutine. */ public function call(StrandInterface $strand) { $method = [$strand->kernel()->api(), $this->name]; if (!is_callable($method)) { $strand->throwException(new BadMethodCallException('The kernel API does not have an operation named "' . $this->name . '".')); return; } $strand->pop(); $arguments = $this->arguments; array_unshift($arguments, $strand); // If the kernel API implementation returns a non-null value it is // treated as a coroutine to be executed. This allows implementation of // kernel API operations to be implemented as generators. $coroutine = call_user_func_array($method, $arguments); if (null !== $coroutine) { $strand->call($coroutine); } }
/** * Finalize the coroutine. * * This method is invoked after the coroutine is popped from the call stack. * * @param StrandInterface $strand The strand that is executing the coroutine. */ public function finalize(StrandInterface $strand) { foreach ($this->substrands as $strand) { $strand->removeListener('exit', [$this, 'onStrandExit']); } }
/** * Stop the coroutine kernel / event-loop. * * The React event-loop can optionally be stopped when all strands have been * terminated. * * @param StrandInterface $strand The currently executing strand. * @param boolean $stopEventLoop Indicates whether or not the React event-loop should also be stopped. */ public function stop(StrandInterface $strand, $stopEventLoop = true) { $strand->terminate(); $strand->kernel()->stop($stopEventLoop); }
/** * Resume execution of a suspended coroutine by passing it an exception. * * @param StrandInterface $strand The strand that is executing the coroutine. * @param Exception $exception The exception to send to the coroutine. */ public function resumeWithException(StrandInterface $strand, Exception $exception) { $strand->throwException($exception); }