Example #1
0
 /**
  * [COROUTINE] Write data to this stream.
  *
  * Execution of the current strand is suspended until the data is sent.
  *
  * Write operations must be exclusive. If concurrent writes are attempted a
  * StreamLockedException is thrown.
  *
  * @param string       $buffer The data to write to the stream.
  * @param integer|null $length The maximum number of bytes to write.
  *
  * @return integer               The number of bytes written.
  * @throws StreamClosedException if the stream is already closed.
  * @throws StreamLockedException if concurrent writes are unsupported.
  * @throws StreamWriteException  if an error occurs while writing to the stream.
  */
 public function write($buffer, $length = null)
 {
     if ($this->strand) {
         throw new StreamLockedException();
     } elseif ($this->isClosed()) {
         throw new StreamClosedException();
     }
     (yield Recoil::suspend(function ($strand) {
         $this->strand = $strand;
         $strand->kernel()->eventLoop()->addWriteStream($this->stream, function ($stream, $eventLoop) use($strand) {
             $eventLoop->removeWriteStream($this->stream);
             $this->strand->resumeWithValue(null);
         });
     }));
     $this->strand = null;
     $exception = null;
     set_error_handler(function ($code, $message, $file, $line) use(&$exception) {
         $exception = new ErrorException($message, 0, $code, $file, $line);
     });
     $bytesWritten = fwrite($this->stream, $buffer, $length ?: strlen($buffer));
     restore_error_handler();
     if (false === $bytesWritten) {
         throw new StreamWriteException($exception);
     }
     (yield Recoil::return_($bytesWritten));
     // @codeCoverageIgnoreStart
 }
Example #2
0
 /**
  * [COROUTINE] Read data from the stream.
  *
  * Execution of the current strand is suspended until data is available or
  * the end of the data stream is reached.
  *
  * Read operations must be exclusive. If concurrent reads are attempted a
  * StreamLockedException is thrown.
  *
  * @param integer $length The maximum number of bytes to read.
  *
  * @return string                The data read from the stream.
  * @throws StreamClosedException if the stream is already closed.
  * @throws StreamLockedException if concurrent reads are unsupported.
  * @throws StreamReadException   if an error occurs while reading from the stream.
  */
 public function read($length)
 {
     if ($this->strand) {
         throw new StreamLockedException();
     } elseif ($this->isClosed()) {
         throw new StreamClosedException();
     }
     (yield Recoil::suspend(function ($strand) {
         $this->strand = $strand;
         $strand->kernel()->eventLoop()->addReadStream($this->stream, function ($stream, $eventLoop) {
             $eventLoop->removeReadStream($stream);
             $this->strand->resumeWithValue(null);
         });
     }));
     $this->strand = null;
     $exception = null;
     set_error_handler(function ($code, $message, $file, $line) use(&$exception) {
         $exception = new ErrorException($message, 0, $code, $file, $line);
     });
     $buffer = fread($this->stream, $length);
     restore_error_handler();
     if (is_resource($this->stream) && feof($this->stream)) {
         fclose($this->stream);
     }
     if (false === $buffer) {
         throw new StreamReadException($exception);
     }
     (yield Recoil::return_($buffer));
     // @codeCoverageIgnoreStart
 }
Example #3
0
 /**
  * [COROUTINE] Write a value to this channel.
  *
  * Execution of the current strand is suspended until the value has been
  * consumed.
  *
  * If the channel is already closed, or is closed while a write operation is
  * pending a ChannelClosedException is thrown.
  *
  * @param mixed $value The value to write to the channel.
  *
  * @throws ChannelClosedException if the channel has been closed.
  */
 public function write($value)
 {
     if ($this->isClosed()) {
         throw new ChannelClosedException();
     }
     if ($this->readStrands->isEmpty()) {
         (yield Recoil::suspend([$this->writeStrands, 'push']));
     }
     $readStrand = $this->readStrands->dequeue();
     $readStrand->resumeWithValue($value);
 }
Example #4
0
 /**
  * Publish a message.
  *
  * @recoil-coroutine
  *
  * @param OutgoingMessage $message The message to publish.
  *
  * The returned promise is resolved when the publisher has sent the message
  * over the network. This does not necessarily mean that the message was
  * successfully routed to a queue or delivered to a consumer, or even that
  * it reached the broker.
  *
  * Additional messages may be published before the promise is resolved, in
  * which case they will be queued in memory.
  *
  * @throws ConnectionException The connection is closed.
  */
 public function publish(OutgoingMessage $message) : Generator
 {
     if ($this->isClosed) {
         throw ChannelClosedException::channelClosed();
     }
     // There is already a message being publish, suspend this strand
     // until we're ready to publish this message ...
     if ($this->currentMessage !== null) {
         (yield Recoil::suspend(function ($strand) {
             $this->strandQueue[$strand->id()] = $strand;
         }, function ($strand) {
             unset($this->strandQueue[$strand->id()]);
         }));
     }
     $this->currentMessage = $message;
     if ($this->channelManager === null) {
         $this->channelManager = (yield $this->connectionManager->acquireChannel());
     }
     (yield $this->channelManager->send(BasicPublishFrame::create($this->exchangeName, $this->fixedRoutingKey ?: $message->routingKey(), $this->options->mandatory, $this->options->immediate)));
     $frame = new BasicHeaderFrame();
     $frame->contentLength = $message->contentLength();
     $message->properties()->to091HeaderFrame($frame);
     (yield $this->channelManager->send($frame));
     if ($frame->contentLength > 0) {
         if ($message instanceof BufferedOutgoingMessage) {
             (yield $this->channelManager->sendContent($message->content()));
         } else {
             assert(false, 'not implemented');
             // $stream = $this->currentMessage->contentStream();
             // $stream->on('data',  [$this, 'onStreamData']);
             // $stream->on('close', [$this, 'onStreamClose']);
             // $stream->on('error', [$this, 'onStreamError']);
             // $stream->resume();
         }
     }
     $this->currentMessage = null;
     if (!empty($this->strandQueue)) {
         foreach ($this->strandQueue as $id => $strand) {
             unset($this->strandQueue[$id]);
             $strand->send();
             break;
         }
     } elseif ($this->channel === null) {
         $this->channelManager->release();
         $this->channelManager = null;
     }
 }
Example #5
0
 /**
  * [COROUTINE] Read data from the stream.
  *
  * Execution of the current strand is suspended until data is available or
  * the end of the data stream is reached.
  *
  * Read operations must be exclusive. If concurrent reads are attempted a
  * StreamLockedException is thrown.
  *
  * @param integer $length The maximum number of bytes to read.
  *
  * @return string                The data read from the stream.
  * @throws StreamClosedException if the stream is already closed.
  * @throws StreamLockedException if concurrent reads are unsupported.
  * @throws StreamReadException   if an error occurs while reading from the stream.
  */
 public function read($length)
 {
     if ($this->strand) {
         throw new StreamLockedException();
     } elseif ($this->isClosed()) {
         throw new StreamClosedException();
     }
     if (!$this->buffer) {
         (yield Recoil::suspend(function ($strand) {
             $this->strand = $strand;
             $this->stream->resume();
         }));
         $this->strand = null;
     }
     if (strlen($this->buffer) > $length) {
         $buffer = substr($this->buffer, 0, $length);
         $this->buffer = substr($this->buffer, $length);
     } else {
         $buffer = $this->buffer;
         $this->buffer = '';
     }
     (yield Recoil::return_($buffer));
     // @codeCoverageIgnoreStart
 }
Example #6
0
 /**
  * [COROUTINE] Close this stream.
  *
  * Closing a stream indicates that no more data will be written to the
  * stream.
  */
 public function close()
 {
     if ($this->strand) {
         $this->strand->resumeWithException(new StreamClosedException());
         $this->strand = null;
         $this->stream->close();
     } else {
         (yield Recoil::suspend(function ($strand) {
             $this->stream->once('close', function () use($strand) {
                 $strand->resumeWithValue(null);
             });
             $this->stream->end();
         }));
     }
 }
Example #7
0
 /**
  * Send a frame to the broker and wait for a response.
  *
  * @recoil-coroutine
  *
  * The frame's channel is automatically set to this channel ID.
  *
  * @param OutgoingFrame $frame  The frame to send.
  * @param integer       $method The method ID of the frame to wait for (*::METHOD_ID constant).
  *
  * @return IncomingFrame The method response.
  */
 public function call(OutgoingFrame $frame, int $method) : Generator
 {
     if ($this->state !== ChannelState::OPEN) {
         throw ChannelException::notOpen($this->channelId);
     }
     $frame->frameChannelId = $this->channelId;
     (yield $this->transport->write($frame));
     return (yield Recoil::suspend(function ($strand) use($method, $frame) {
         $id = $strand->id();
         $this->callers[$method][$id] = $strand;
         $this->callersBySentMethod[$frame::METHOD_ID][$id] = $method;
         $this->callersByWaitMethod[$method][$id] = $frame::METHOD_ID;
     }, function ($strand) use($method, $frame) {
         $id = $strand->id();
         unset($this->callers[$method][$id], $this->callersBySentMethod[$frame::METHOD_ID][$id], $this->callersByWaitMethod[$method][$id]);
     }));
 }