示例#1
0
 private function createPoll() : Io
 {
     return Loop\poll(\eio_get_event_stream(), function () {
         while (\eio_npending()) {
             \eio_poll();
         }
     }, true);
 }
示例#2
0
 /**
  * @coroutine
  *
  * @param string $connectionString
  * @param float|int $timeout
  *
  * @return \Generator
  *
  * @resolve \Icicle\Postgres\Connection
  *
  * @throws \Icicle\Postgres\Exception\FailureException
  */
 function connect(string $connectionString, float $timeout = 0) : \Generator
 {
     if (!($connection = @\pg_connect($connectionString, \PGSQL_CONNECT_ASYNC | \PGSQL_CONNECT_FORCE_NEW))) {
         throw new FailureException('Failed to create connection resource');
     }
     if (\pg_connection_status($connection) === \PGSQL_CONNECTION_BAD) {
         throw new FailureException(\pg_last_error($connection));
     }
     if (!($socket = \pg_socket($connection))) {
         throw new FailureException('Failed to access connection socket');
     }
     $delayed = new Delayed();
     $callback = function ($resource, bool $expired) use(&$poll, &$await, $connection, $delayed, $timeout) {
         try {
             if ($expired) {
                 throw new TimeoutException('Connection attempt timed out.');
             }
             switch (\pg_connect_poll($connection)) {
                 case \PGSQL_POLLING_READING:
                     return;
                     // Connection not ready, poll again.
                 // Connection not ready, poll again.
                 case \PGSQL_POLLING_WRITING:
                     $await->listen($timeout);
                     return;
                     // Still writing...
                 // Still writing...
                 case \PGSQL_POLLING_FAILED:
                     throw new FailureException('Could not connect to PostgreSQL server');
                 case \PGSQL_POLLING_OK:
                     $poll->free();
                     $await->free();
                     $delayed->resolve(new BasicConnection($connection, $resource));
                     return;
             }
         } catch (\Throwable $exception) {
             $poll->free();
             $await->free();
             \pg_close($connection);
             $delayed->reject($exception);
         }
     };
     $poll = Loop\poll($socket, $callback, true);
     $await = Loop\await($socket, $callback);
     $poll->listen($timeout);
     $await->listen($timeout);
     return (yield $delayed);
 }
示例#3
0
 /**
  * Connection constructor.
  *
  * @param resource $handle PostgreSQL connection handle.
  * @param resource $socket PostgreSQL connection stream socket.
  */
 public function __construct($handle, $socket)
 {
     $this->handle = $handle;
     $this->poll = Loop\poll($socket, static function ($resource, bool $expired, Io $poll) use($handle) {
         /** @var \Icicle\Awaitable\Delayed $delayed */
         $delayed = $poll->getData();
         if (!\pg_consume_input($handle)) {
             $delayed->reject(new FailureException(\pg_last_error($handle)));
             return;
         }
         if (!\pg_connection_busy($handle)) {
             $delayed->resolve(\pg_get_result($handle));
             return;
         }
         $poll->listen();
         // Reading not done, listen again.
     });
     $this->await = Loop\await($socket, static function ($resource, bool $expired, Io $await) use($handle) {
         $flush = \pg_flush($handle);
         if (0 === $flush) {
             $await->listen();
             // Not finished sending data, listen again.
             return;
         }
         if (false === $flush) {
             /** @var \Icicle\Awaitable\Delayed $delayed */
             $delayed = $await->getData();
             $delayed->reject(new FailureException(\pg_last_error($handle)));
         }
     });
     $this->onCancelled = static function () use($handle) {
         \pg_cancel_query($handle);
     };
     $this->executeCallback = function (string $name, array $params) : \Generator {
         return $this->createResult(yield from $this->send('pg_send_execute', $name, $params));
     };
 }
示例#4
0
 /**
  * @param resource $resource
  * @param \SplQueue $readQueue
  *
  * @return \Icicle\Loop\Watcher\Io
  */
 private function createPoll($resource, \SplQueue $readQueue) : Io
 {
     $length =& $this->length;
     return Loop\poll($resource, static function ($resource, bool $expired) use(&$length, $readQueue) {
         /** @var \Icicle\Awaitable\Delayed $delayed */
         $delayed = $readQueue->shift();
         try {
             if ($expired) {
                 throw new TimeoutException('The datagram timed out.');
             }
             $data = stream_socket_recvfrom($resource, $length, 0, $peer);
             // Having difficulty finding a test to cover this scenario, but the check seems appropriate.
             if (false === $data) {
                 // Reading failed, so close datagram.
                 $message = 'Failed to read from datagram.';
                 if ($error = error_get_last()) {
                     $message .= sprintf(' Errno: %d; %s', $error['type'], $error['message']);
                 }
                 throw new FailureException($message);
             }
             list($address, $port) = Socket\parseName($peer);
             $result = [$address, $port, $data];
             $delayed->resolve($result);
         } catch (Throwable $exception) {
             $delayed->reject($exception);
         }
     });
 }
示例#5
0
    /**
     * @throws \Icicle\Concurrent\Exception\StatusError If the process is already running.
     */
    public function start()
    {
        if (null !== $this->promise) {
            throw new StatusError('The process has already been started.');
        }

        $fd = [
            ['pipe', 'r'], // stdin
            ['pipe', 'w'], // stdout
            ['pipe', 'a'], // stderr
            ['pipe', 'w'], // exit code pipe
        ];

        $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->promise = new Promise(function (callable $resolve, callable $reject) use ($stream) {
            $this->poll = Loop\poll($stream, function ($resource) use ($resolve, $reject) {
                if (feof($resource)) {
                    $reject(new ProcessException('Process ended unexpectedly.'));
                } else {
                    $code = fread($resource, 1);

                    if (!strlen($code) || !is_numeric($code)) {
                        $reject(new ProcessException('Process ended without providing a status code.'));
                    } else {
                        $resolve((int) $code);
                    }
                }

                fclose($resource);

                if (is_resource($this->process)) {
                    proc_close($this->process);
                    $this->process = null;
                }

                $this->stdin->close();
                $this->poll->free();
            });

            $this->poll->unreference();
            $this->poll->listen();
        });
    }
示例#6
0
 /**
  * @param resource $resource
  * @param \SplQueue $queue
  *
  * @return \Icicle\Loop\Watcher\Io
  */
 private function createPoll($resource, \SplQueue $queue) : Io
 {
     return Loop\poll($resource, static function ($resource, bool $expired) use($queue) {
         /** @var \Icicle\Awaitable\Delayed $delayed */
         $delayed = $queue->shift();
         if ($expired) {
             $delayed->reject(new TimeoutException('The connection timed out.'));
             return;
         }
         $delayed->resolve();
     });
 }
示例#7
0
 /**
  * @param resource $resource
  * @param \SplQueue $queue
  *
  * @return \Icicle\Loop\Watcher\Io
  */
 private function createPoll($resource, \SplQueue $queue) : Io
 {
     return Loop\poll($resource, static function ($resource, bool $expired, Io $poll) use($queue) {
         // Error reporting suppressed since stream_socket_accept() emits E_WARNING on client accept failure.
         $socket = @stream_socket_accept($resource, 0);
         // Timeout of 0 to be non-blocking.
         // Having difficulty finding a test to cover this scenario, but it has been seen in production.
         if (!$socket) {
             $poll->listen();
             // Accept failed, let's go around again.
             return;
         }
         /** @var \Icicle\Awaitable\Delayed $delayed */
         $delayed = $queue->shift();
         $delayed->resolve($socket);
     });
 }