/** * Throws an error in the observable. * * @param \Throwable $reason */ public function fail(\Throwable $reason) { if (null !== $this->started) { $this->started->reject(new CompletedError()); } $this->delayed->reject($reason); }
/** * Marks the observable as complete with the given error. * * @param \Throwable $exception */ public function fail(\Throwable $exception) { if (null === $this->observable) { return; } $this->observable = null; $this->failed = true; $this->delayed->reject($exception); }
/** * @throws \Icicle\Concurrent\Exception\ProcessException If starting the process fails. * @throws \Icicle\Concurrent\Exception\StatusError If the process is already running. */ public function start() { if (null !== $this->delayed) { throw new StatusError('The process has already been started.'); } $this->delayed = new Delayed(); $fd = [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'a'], ['pipe', 'w']]; $nd = 0 === strncasecmp(PHP_OS, 'WIN', 3) ? 'NUL' : '/dev/null'; $command = sprintf('(%s) 3>%s; code=$?; echo $code >&3; exit $code', $this->command, $nd); $this->process = proc_open($command, $fd, $pipes, $this->cwd ?: null, $this->env ?: null, $this->options); if (!is_resource($this->process)) { throw new ProcessException('Could not start process.'); } $this->oid = getmypid(); $status = proc_get_status($this->process); if (!$status) { proc_close($this->process); $this->process = null; throw new ProcessException('Could not get process status.'); } $this->pid = $status['pid']; $this->stdin = new WritablePipe($pipes[0]); $this->stdout = new ReadablePipe($pipes[1]); $this->stderr = new ReadablePipe($pipes[2]); $stream = $pipes[3]; stream_set_blocking($stream, 0); $this->poll = Loop\poll($stream, function ($resource) { if (!is_resource($resource) || feof($resource)) { $this->close($resource); $this->delayed->reject(new ProcessException('Process ended unexpectedly.')); } else { $code = fread($resource, 1); $this->close($resource); if (!strlen($code) || !is_numeric($code)) { $this->delayed->reject(new ProcessException('Process ended without providing a status code.')); } else { $this->delayed->resolve((int) $code); } } $this->poll->free(); }); }
/** * {@inheritdoc} */ public function read(int $length = 0, string $byte = null, float $timeout = 0) : \Generator { while (null !== $this->delayed) { (yield $this->delayed); } if (!$this->isReadable()) { throw new UnreadableException('The stream is no longer readable.'); } $this->length = $length; if (0 > $this->length) { throw new InvalidArgumentError('The length should be a positive integer.'); } $this->byte = strlen($byte) ? $byte[0] : null; if (!$this->buffer->isEmpty()) { $data = $this->remove(); if (0 !== $this->hwm && $this->buffer->getLength() <= $this->hwm) { while (!$this->queue->isEmpty()) { /** @var \Icicle\Awaitable\Delayed $delayed */ $delayed = $this->queue->shift(); $delayed->resolve(); } } if (!$this->writable && $this->buffer->isEmpty()) { $this->free(); } return $data; } $awaitable = $this->delayed = new Delayed(); if ($timeout) { $awaitable = $this->delayed->timeout($timeout); } try { $data = (yield $awaitable); } finally { $this->delayed = null; } return $data; }
/** * Notifies the placeholder that the consumer is ready. */ public function ready() { if (0 === --$this->waiting) { $this->delayed->resolve(); } }
/** * Returns an observable that emits a value every $interval seconds, up to $count times (or indefinitely if $count * is 0). The value emitted is an integer of the number of times the observable emitted a value. * * @param float|int $interval Time interval between emitted values in seconds. * @param int $count Use 0 to emit values indefinitely. * * @return \Icicle\Observable\Observable */ function interval(float $interval, int $count = 0) : Observable { return new Emitter(function (callable $emit) use($interval, $count) : \Generator { if (0 > $count) { throw new InvalidArgumentError('The number of times to emit must be a non-negative value.'); } $start = microtime(true); $i = 0; $delayed = new Delayed(); $timer = Loop\periodic($interval, function () use(&$delayed, &$i) { $delayed->resolve(++$i); $delayed = new Delayed(); }); try { while (0 === $count || $i < $count) { yield from $emit($delayed); } } finally { $timer->stop(); } return microtime(true) - $start; }); }
/** * Throws an error in the observable. * * @param \Throwable $reason */ public function fail(\Throwable $reason) { $this->delayed->reject($reason); }