/** * Remove and return items from the queue * * @param int $n maximum number of items to be popped * @param int $timeout maximum time (in seconds) to wait for new items when the queue is empty * @return PriorityItem[] array of at most $n items popped from the queue */ public function dequeue($n = 1, $timeout = 0) { //do a non-blocking dequeue, return on success $result = $this->queue->dequeue($n, 0); if (count($result) || $timeout < 1) { return $result; } $start = microtime(true); //if nothing popped: while timeout is not reached do the following: while (($loopStart = microtime(true)) < $start + $timeout + 1) { $result = $this->queue->dequeue($n, 0); if (count($result)) { return $result; } //find $t0 and $best as declared in $this->enqueue() $first = $this->redis->zrange($this->name, 0, 0, 'WITHSCORES'); if (count($first)) { $t0 = array_values($first)[0]; $best = array_keys($first)[0]; } else { $t0 = $start + $timeout; $best = 0; } //$x = blpop "{$this->name}:$best", $t0 //if $x: dequeue, if something is dequeued return $timeToWait = max(1, ceil(min($timeout - $loopStart + $start, $t0 - $loopStart))); $items = $this->redis->blpop([$this->name . ':' . $best], $timeToWait); if (is_null($items)) { continue; } $items = [$items[1]]; if ($n > 1) { $lua = <<<LUA local values = redis.call('lrange', KEYS[1], 0, KEYS[2] - 1) redis.call('ltrim', KEYS[1], KEYS[2], - 1) return values LUA; $remainingItems = $this->redis->eval($lua, 2, $this->name . ':' . $best, $n - 1); $items = array_merge($items, $remainingItems); } $result = $this->queue->dequeue(count($items)); if (count($result)) { return $result; } } return []; }
public function test_blocking_pop_from_empty_queue() { $start = time(); $this->assertEquals([], $this->queue->dequeue(1, 1)); $this->assertGreaterThan($start, time()); }