/** * {@inheritDoc} */ public function write($messageContent) { $writer = new Awaitable(); $message = new Message($messageContent); $this->writersQueue->enqueue($writer); $this->messageQueue->enqueue($message); if ($this->buffer) { while ($this->messageQueue->bottom() !== $message || !$this->buffer->isWriteable()) { (yield $writer->await()); } $this->buffer->write($this->messageQueue->dequeue()); $this->notifyAwaiting($this->readersQueue); return; } $this->notifyAwaiting($this->readersQueue); while ($this->readersQueue->count() <= $this->indexOf($message, $this->messageQueue)) { (yield $writer->await()); } }
/** * @inheritDoc */ public function __invoke(ObservableInterface $observable, ObserverInterface $observer, SchedulerInterface $scheduler = null) { if ($this->scheduler !== null) { $scheduler = $this->scheduler; } if ($scheduler === null) { throw new \Exception("You must use a scheduler that support non-zero delay."); } /** @var AnonymousObservable $observable */ $disp = $observable->materialize()->timestamp()->map(function (Timestamped $x) { return new Timestamped($x->getTimestampMillis() + $this->delayTime, $x->getValue()); })->subscribe(new CallbackObserver(function (Timestamped $x) use($scheduler, $observer) { if ($x->getValue() instanceof Notification\OnErrorNotification) { $x->getValue()->accept($observer); return; } $this->queue->enqueue($x); if ($this->schedulerDisposable === null) { $doScheduledStuff = function () use($observer, $scheduler, &$doScheduledStuff) { while (!$this->queue->isEmpty() && $scheduler->now() >= $this->queue->bottom()->getTimestampMillis()) { /** @var Timestamped $item */ $item = $this->queue->dequeue(); /** @var Notification $materializedValue */ $materializedValue = $item->getValue(); $materializedValue->accept($observer); } if ($this->queue->isEmpty()) { $this->schedulerDisposable = null; return; } $this->schedulerDisposable = $scheduler->schedule($doScheduledStuff, $this->queue->bottom()->getTimestampMillis() - $scheduler->now()); }; $doScheduledStuff(); } }, [$observer, 'onError']), $scheduler); return new CallbackDisposable(function () use($disp) { if ($this->schedulerDisposable) { $this->schedulerDisposable->dispose(); } $disp->dispose(); }); }
public function leastHops($origin, $destination) { // mark all nodes as unvisited foreach ($this->graph as $key => $vertex) { $this->visited[$key] = false; } // create an empty queue $q = new SplQueue(); // enqueue the origin vertex and mark as visited $q->enqueue($origin); $this->visited[$origin] = true; // an array stack to track the path back from each node $path = array(); $path[$origin] = new SplDoublyLinkedList(); $path[$origin]->setIteratorMode(SplDoublyLinkedList::IT_MODE_FIFO | SplDoublyLinkedList::IT_MODE_KEEP); $path[$origin]->push($origin); $found = false; // while queue is not empty and destination not found while (!$q->isEmpty() && $q->bottom() != $destination) { $t = $q->dequeue(); if (!empty($this->graph[$t])) { // for each adjacent neighbor foreach ($this->graph[$t] as $vertex) { if (!$this->visited[$vertex]) { // if not yet visited, enqueue vertex and mark as visited $q->enqueue($vertex); $this->visited[$vertex] = true; // add vertex to current node path $path[$vertex] = clone $path[$t]; $path[$vertex]->push($vertex); } } } } if (isset($path[$destination])) { echo "{$origin} to {$destination} in ", count($path[$destination]) - 1, " hops\n"; $sep = ''; foreach ($path[$destination] as $vertex) { echo $sep, $vertex; $sep = '->'; } echo "\n"; } else { echo "No route from {$origin} to {$destination}\n"; } }
/** * {@inheritdoc} */ public function receive(int $length = 0, float $timeout = 0) : \Generator { while (!$this->readQueue->isEmpty()) { /** @var \Icicle\Awaitable\Delayed $delayed */ $delayed = $this->readQueue->bottom(); (yield $delayed); // Wait for previous read to complete. } if (!$this->isOpen()) { throw new UnavailableException('The datagram is no longer readable.'); } $this->length = (int) $length; if (0 > $this->length) { throw new InvalidArgumentError('The length must be a non-negative integer.'); } elseif (0 === $this->length) { $this->length = self::MAX_PACKET_SIZE; } $this->readQueue->push($delayed = new Delayed($this->onReceiveCancelled)); $this->poll->listen($timeout); return (yield $delayed); }
/** * {@inheritdoc} */ public function accept(bool $autoClose = true) : \Generator { while (!$this->queue->isEmpty()) { /** @var \Icicle\Awaitable\Delayed $delayed */ $delayed = $this->queue->bottom(); (yield $delayed); // Wait for previous accept to complete. } if (!$this->isOpen()) { throw new UnavailableException('The server has been closed.'); } // Error reporting suppressed since stream_socket_accept() emits E_WARNING on client accept failure. $socket = @stream_socket_accept($this->getResource(), 0); // Timeout of 0 to be non-blocking. if ($socket) { return $this->createSocket($socket, $autoClose); } $this->queue->push($delayed = new Delayed($this->onCancelled)); $this->poll->listen(); return $this->createSocket((yield $delayed), $autoClose); }
/** * @coroutine * * Returns a coroutine fulfilled when there is data available to read in the internal stream buffer. Note that * this method does not consider data that may be available in the internal buffer. This method can be used to * implement functionality that uses the stream socket resource directly. * * @param float|int $timeout Number of seconds until the returned coroutine is rejected with a TimeoutException * if no data is received. Use 0 for no timeout. * * @return \Generator * * @resolve string Empty string. * * @throws \Icicle\Awaitable\Exception\TimeoutException If the operation times out. * @throws \Icicle\Stream\Exception\FailureException If the stream buffer is not empty. * @throws \Icicle\Stream\Exception\UnreadableException If the stream is no longer readable. * @throws \Icicle\Stream\Exception\ClosedException If the stream has been closed. */ public function poll(float $timeout = 0) : \Generator { while (!$this->queue->isEmpty()) { /** @var \Icicle\Awaitable\Delayed $delayed */ $delayed = $this->queue->bottom(); (yield $delayed); // Wait for previous read to complete. } if (!$this->isReadable()) { throw new UnreadableException('The stream is no longer readable.'); } if ('' !== $this->buffer) { throw new FailureException('Stream buffer is not empty. Perform another read before polling.'); } $this->queue->push($delayed = new Delayed($this->onCancelled)); $this->poll->listen($timeout); (yield $delayed); if ('' !== $this->buffer) { throw new FailureException('Data unshifted to stream buffer while polling.'); } return ''; // Resolve with empty string. }
private function constructAST(Queue $tokens) : AST\Node { if ($tokens->isEmpty()) { throw new ParseException("Unexpected end of file"); } $stack = new Stack(); while (!$tokens->isEmpty()) { $token = $tokens->bottom(); if ($this->isNumber($token)) { $tokens->dequeue(); $stack->push(new AST\Constant($token->getLine(), (double) $token->getContent())); } else { if ($this->isVariable($token)) { $tokens->dequeue(); $stack->push(new AST\Variable($token->getLine(), $token->getContent())); } else { if ($this->isOperator($token)) { $tokens->dequeue(); $right = $stack->pop(); $left = $stack->pop(); $stack->push(new AST\Operator($left->getLine(), $token->getId(), $left, $right)); } else { if ($this->isFunctionName($token)) { $tokens->dequeue(); $arguments = []; for ($i = 0; $i < FUNCTIONS[$token->getContent()]['arity']; $i++) { if ($stack->isEmpty()) { throw new ParseException("Not enough arguments on stack for '{$token->getContent()}', some function is missing an argument"); } $arguments[] = $stack->pop(); } // sin(1,2) would be [2 1 sin] on the stack $arguments = \array_reverse($arguments); $stack->push(new AST\FunctionCall($token->getLine(), $token->getContent(), $arguments)); } else { throw new ParseException("Unexpected token {$token->getName()} on line {$token->getLine()}"); } } } } } if ($stack->isEmpty()) { throw new ParseException("??????"); } if ($stack->count() !== 1) { throw new ParseException("Missing operator"); } return $stack->pop(); }
/** * Convert over a number of hops if possible * * Breadth-first search from http://www.sitepoint.com/data-structures-4/ * * @return SplDoublyLinkedList|array */ protected function getIndirectConversion(Unit $from, Unit $to) { $origin = $from->getAbbr(); $destination = $to->getAbbr(); // Mark all nodes as unvisited $visited = array(); foreach ($this->conversions as $vertex => $adj) { $visited[$vertex] = false; } // Create a queue $q = new \SplQueue(); // Enqueue the origin vertex and mark as visited $q->enqueue($origin); $visited[$origin] = true; // Create a path that can be back-tracked $path = array(); $path[$origin] = new \SplDoublyLinkedList(); $path[$origin]->setIteratorMode(\SplDoublyLinkedList::IT_MODE_FIFO | \SplDoublyLinkedList::IT_MODE_KEEP); $found = false; while (!$q->isEmpty() && $q->bottom() != $destination) { $t = $q->dequeue(); if (!empty($this->conversions[$t])) { // For each adjacent neighbour, foreach ($this->conversions[$t] as $vertex => $conv) { if (!array_key_exists($vertex, $visited) || !$visited[$vertex]) { // Mark as visited and enqueue $q->enqueue($vertex); $visited[$vertex] = true; // Add to current path $path[$vertex] = clone $path[$t]; $path[$vertex]->push($conv); } } } } if (isset($path[$destination])) { return $path[$destination]; } else { return array(); } }
/** * @param resource $resource * @param \SplQueue $writeQueue * * @return \Icicle\Loop\Watcher\Io */ private function createAwait($resource, \SplQueue $writeQueue) : Io { return Loop\await($resource, static function ($resource, bool $expired, Io $await) use($writeQueue) { /** @var \Icicle\Awaitable\Delayed $delayed */ list($data, $previous, $timeout, $delayed) = $writeQueue->shift(); if ($expired) { $delayed->reject(new TimeoutException('Writing to the socket timed out.')); return; } $length = strlen($data); if (0 === $length) { $delayed->resolve($previous); } else { // Error reporting suppressed since fwrite() emits E_WARNING if the pipe is broken or the buffer is full. $written = @fwrite($resource, $data, self::CHUNK_SIZE); if (false === $written || 0 === $written) { $message = 'Failed to write to stream.'; if ($error = error_get_last()) { $message .= sprintf(' Errno: %d; %s', $error['type'], $error['message']); } $delayed->reject(new FailureException($message)); return; } if ($length <= $written) { $delayed->resolve($written + $previous); } else { $data = substr($data, $written); $written += $previous; $writeQueue->unshift([$data, $written, $timeout, $delayed]); } } if (!$writeQueue->isEmpty()) { list(, , $timeout) = $writeQueue->bottom(); $await->listen($timeout); } }); }
public function stateStatement(Token $token) { $lval = strtolower($token->value); switch ($token->type) { case Token::LCURLY: case Token::RCURLY: $this->curlyLevel += $token->type === Token::LCURLY ? +1 : -1; case Token::SEMICOLON: $this->appendStatement($token); $this->dumpStatement(); $this->transition(self::PHP); return false; case Token::DOT: // check if it' just before a "should" token $next = $this->skip(Token::WHITESPACE, Token::EOL); if ($next->type === Token::IDENT && strtolower($next->value) === 'should') { // Replace it with a single whitespace $token = new Token(Token::WHITESPACE, ' '); $this->appendStatement($token); return $next; } $this->appendStatement($token); return $next; case $token->value === '__DIR__': $token->value = self::SPEC_CLASS . '::dir(__DIR__)'; $this->appendStatement($token); return false; case $lval === 'should': // Flush captured non-statement tokens while (!$this->statement->isEmpty()) { $token = $this->statement->bottom(); if ($token->type === Token::COMMENT || $token->type === Token::WHITESPACE || $token->type === Token::EOL) { $this->write($token->value); $this->statement->shift(); } else { break; } } // Define the expectation wrapper $this->write(self::SPEC_CLASS . '::expect('); $this->dumpStatement(); $this->write(')->'); $this->transition(self::SHOULD); return false; case $lval === '$this': switch (strtoupper($this->blocks->top())) { case 'DESCRIBE': case 'BEFORE': case 'AFTER': $token->value = self::SPEC_CLASS . '::suite()'; break; case 'BEFORE_EACH': case 'AFTER_EACH': case 'IT': $token->value = self::SPEC_CLASS . '::test()'; break; } default: if ($token->token === T_CLOSE_TAG) { $this->dumpStatement(); $this->transition(self::PHP); return $token; } $this->appendStatement($token); return false; } }
private function updateLimitQueue(SplQueue $queue, $interval, $call_limit) { while (!$queue->isEmpty()) { /* Three possibilities here. 1: There are timestamps outside the window of the interval, which means that the requests associated with them were long enough ago that they can be removed from the queue. 2: There have been more calls within the previous interval of time than are allowed by the rate limit, in which case the program blocks to ensure the rate limit isn't broken. 3: There are openings in window, more requests are allowed, and the program continues.*/ $timeSinceOldest = time() - $queue->bottom(); // I recently learned that the "bottom" of the // queue is the beginning of the queue. Go figure. // Remove timestamps from the queue if they're older than // the length of the interval if ($timeSinceOldest > $interval) { $queue->dequeue(); } elseif ($queue->count() >= $call_limit) { if ($timeSinceOldest < $interval) { //order of ops matters echo "sleeping for" . ($interval - $timeSinceOldest + 1) . " seconds\n"; sleep($interval - $timeSinceOldest); } } else { break; } } // Add current timestamp to back of queue; this represents // the current request. $queue->enqueue(time()); }
/** * @param $toVertex * @param \SplQueue $queue * @return bool */ private function notReached($toVertex, $queue) { return !$queue->isEmpty() && $queue->bottom() != $toVertex; }
/** * peek at bottom of queue * * @return mixed */ public function peekBottom() { return $this->queue->bottom(); }
/** * Return a current result. * * @see Iterator * @return mixed */ public function current() { return $this->results->bottom(); }