/** * @coroutine * * @return \Generator * * @resolve \Icicle\Postgres\Connection */ private function pop() : \Generator { while (null !== $this->awaitable) { try { (yield $this->awaitable); // Prevent simultaneous connection creation. } catch (\Throwable $exception) { // Ignore failure or cancellation of other operations. } } if ($this->idle->isEmpty()) { try { if ($this->connections->count() >= $this->getMaxConnections()) { // All possible connections busy, so wait until one becomes available. $this->awaitable = new Delayed(); (yield $this->awaitable); } else { // Max connection count has not been reached, so open another connection. $this->awaitable = new Coroutine($this->createConnection()); $this->addConnection((yield $this->awaitable)); } } finally { $this->awaitable = null; } } // Shift a connection off the idle queue. return $this->idle->shift(); }
/** * {@inheritdoc} */ public function tick() { if (!$this->queue->isEmpty()) { $immediate = $this->queue->shift(); $this->immediates->detach($immediate); // Execute the immediate. $immediate->call(); return true; } return false; }
/** * Executes each callback that was in the queue when this method is called up to the maximum depth. * * @return int Number of functions called. */ public function call() : int { $count = 0; while (!$this->queue->isEmpty() && (0 === $this->maxDepth || $count < $this->maxDepth)) { list($callback, $args) = $this->queue->shift(); ++$count; if (empty($args)) { $callback(); } else { call_user_func_array($callback, $args); } } return $count; }
/** * {@inheritdoc} */ public function close() { if ($this->open && $this->worker->isRunning()) { $coroutine = new Coroutine($this->worker->enqueue(new Internal\FileTask('fclose', [], $this->id))); $coroutine->done(null, [$this->worker, 'kill']); } if (!$this->queue->isEmpty()) { $exception = new FileException('The file was closed.'); do { $this->queue->shift()->cancel($exception); } while (!$this->queue->isEmpty()); } $this->open = false; $this->writable = false; }
/** * @param $db_sock * @return bool */ public function onSQLReady($db_sock) { $task = empty($this->work_pool[$db_sock]) ? null : $this->work_pool[$db_sock]; if (empty($task)) { #echo "MySQLi Warning: Maybe SQLReady receive a Close event , such as Mysql server close the socket !\n"; if (isset($this->idle_pool[$db_sock])) { $this->removeConnection($this->idle_pool[$db_sock]); } return false; } /** * @var \mysqli $mysqli */ $mysqli = $task['mysql']['object']; $callback = $task['callback']; if ($result = $mysqli->reap_async_query()) { call_user_func($callback, $mysqli, $result); if (is_object($result)) { mysqli_free_result($result); } } else { call_user_func($callback, $mysqli, $result); #echo "MySQLi Error: " . mysqli_error($mysqli) . "\n"; } //release mysqli object unset($this->work_pool[$db_sock]); if ($this->pool_size < $this->connection_num) { //减少连接数 $this->removeConnection($task['mysql']); } else { $this->idle_pool[$task['mysql']['socket']] = $task['mysql']; //fetch a request from wait queue. if (count($this->wait_queue) > 0) { $idle_n = count($this->idle_pool); for ($i = 0; $i < $idle_n; $i++) { $new_task = $this->wait_queue->shift(); $this->doQuery($new_task['sql'], $new_task['callback']); } } } }
/** * {@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; }
public function send($data = null) { //锁定状态写入队列 if (!empty($data)) { $this->queue->push($data); } if ($this->reconn) { Trace::debug("*********cli {$this->fd} reconn \n"); $this->cli->connect($this->conf['ip'], $this->conf['port']); $this->reconn = false; $this->lock = true; } if ($this->queue->isEmpty()) { $this->lock = false; } elseif (!$this->lock) { $this->lock = true; $data = $this->queue->shift(); Trace::debug("*********cli {$this->fd} send " . strlen($data) . "\n==================\n" . substr($data, 0, 50) . "...\n=============="); Trace::info(sprintf("Host: %-25s %s", $this->conf['host'], strstr($data, "\n", true))); //;'Host:' . $this->conf['host'] . strstr($data, "\n", true) $this->cli->send($data); } }
/** * @param string $data * * @return \Icicle\Awaitable\Delayed * * @throws \Icicle\File\Exception\FileException */ private function push(string $data) : Awaitable { $length = strlen($data); $delayed = new Delayed(); \eio_write($this->handle, $data, $length, $this->append ? $this->size : $this->position, null, function (Delayed $delayed, $result, $req) use($length) { if (-1 === $result) { $delayed->reject(new FileException(sprintf('Writing to the file failed: %s.', \eio_get_last_error($req)))); } elseif ($this->queue->isEmpty()) { $delayed->reject(new FileException('No pending write, the file may have been closed.')); } else { $this->queue->shift(); if ($this->append) { $this->size += $result; } else { $this->position += $result; if ($this->position > $this->size) { $this->size = $this->position; } } $delayed->resolve($result); } }, $delayed); return $delayed; }
/** * Converts a function accepting a callback ($emitter) that invokes the callback when an event is emitted into an * observable that emits the arguments passed to the callback function each time the callback function would be * invoked. * * @param callable<(mixed ...$args)> $emitter Function accepting a callback that periodically emits events. * @param int $index Position of callback function in emitter function argument list. * @param mixed ...$args Other arguments to pass to emitter function. * * @return \Icicle\Observable\Observable */ function observe(callable $emitter, int $index = 0, ...$args) : Observable { return new Emitter(function (callable $emit) use($emitter, $index, $args) : \Generator { $queue = new \SplQueue(); /** @var \Icicle\Awaitable\Delayed $delayed */ $callback = function (...$args) use(&$delayed, $queue) { if (null !== $delayed) { $delayed->resolve($args); $delayed = null; } else { $queue->push($args); } }; if (count($args) < $index) { throw new InvalidArgumentError('Too few arguments given to function.'); } array_splice($args, $index, 0, [$callback]); $emitter(...$args); while (true) { if ($queue->isEmpty()) { $delayed = new Delayed(); yield from $emit($delayed); } else { yield from $emit($queue->shift()); } } }); }
/** * @param $db * @param null $_result * @return bool */ public function onSQLReady($db, $_result = null) { $db_sock = $this->haveSwooleAsyncMySQL ? $db->sock : $db; $task = empty($this->work_pool[$db_sock]) ? null : $this->work_pool[$db_sock]; //SQL返回了错误 if ($_result === false) { //连接已关闭 if (empty($task) or $this->haveSwooleAsyncMySQL and $db->_connected == false) { unset($this->work_pool[$db_sock]); $this->removeConnection($task['mysql']); //创建连接成功 if ($this->createConnection() === 0) { $this->doQuery($task['sql'], $task['callback']); } else { $this->wait_queue->push(array('sql' => $task['sql'], 'callback' => $task['callback'])); } return; } } /** * @var \mysqli $mysqli */ $mysqli = $task['mysql']['object']; $callback = $task['callback']; if ($this->haveSwooleAsyncMySQL) { call_user_func($callback, $mysqli, $_result); } else { $mysqli->_affected_rows = $mysqli->affected_rows; $mysqli->_insert_id = $mysqli->insert_id; $mysqli->_errno = $mysqli->errno; $mysqli->_error = $mysqli->error; if ($_sql_result = $mysqli->reap_async_query()) { if ($_sql_result instanceof \mysqli_result) { $result = $_sql_result->fetch_all(); } else { $result = $_sql_result; } call_user_func($callback, $mysqli, $result); if (is_object($result)) { mysqli_free_result($result); } } else { call_user_func($callback, $mysqli, false); } } //release mysqli object unset($this->work_pool[$db_sock]); if ($this->pool_size < $this->connection_num) { //减少连接数 $this->removeConnection($task['mysql']); } else { deQueue: $this->idle_pool[$task['mysql']['socket']] = $task['mysql']; $queue_count = count($this->wait_queue); //fetch a request from wait queue. if ($queue_count > 0) { $task_n = count($this->idle_pool); if ($task_n > $queue_count) { $task_n = $queue_count; } for ($i = 0; $i < $task_n; $i++) { $new_task = $this->wait_queue->shift(); $this->doQuery($new_task['sql'], $new_task['callback']); } } } }
public function fire($job, $message) { $this->warnings = array(); $commit = $message['commit']; $hostType = $message['hostType']; $siteId = $message['siteId']; $dc = new DC($siteId); $staticDir = $dc->get(DC::STATIC_DIR); if (isset($message['type']) && $message['type'] == self::TYPE_PULL_REQUEST) { $id = $message['id']; $this->type = self::TYPE_PULL_REQUEST; $this->pr = new PullRequestDeploy($siteId); $this->prDeployInfo = $this->pr->get($id); $root = (new SystemConfig())->get(SystemConfig::WORK_ROOT_FIELD) . '/' . $siteId . '/pull_requests'; $commitPath = "{$root}/commit/{$commit}"; } else { $this->type = self::TYPE_NORMAL_DEPLOY; $this->dfManger = new DeployInfo($siteId); $id = $message['id']; $this->deployInfo = $this->dfManger->get($id); $root = (new SystemConfig())->get(SystemConfig::WORK_ROOT_FIELD) . '/' . $siteId . '/commit'; $commitPath = "{$root}/{$commit}"; } $LOCAL_STATIC_DIR = "{$commitPath}/{$staticDir}"; $LOCAL_DIR = $commitPath; $remoteUser = $dc->get(DC::REMOTE_USER); $remoteOwner = $dc->get(DC::REMOTE_OWNER); $RSYNC_EXCLUDE = "{$commitPath}/" . $dc->get(DC::RSYNC_EXCLUDE); $REMOTE_STATIC_DIR = $dc->get(DC::REMOTE_STATIC_DIR); $REMOTE_DIR = $dc->get(DC::REMOTE_APP_DIR); $staticScript = ScriptCommand::complie($dc->get(DC::DEPLOY_STATIC_SCRIPT), $siteId); $webScript = ScriptCommand::complie($dc->get(DC::DEPLOY_WEB_SCRIPT), $siteId); $this->updateStatus('Deploying'); Log::info("\n---------------------------\njob id : {$job->getJobId()} start "); Log::info("commit deploy: {$commit}"); //本地同步锁,不能在同一个commit下同步 $redis = app('redis')->connection(); $commitLock = new \Eleme\Rlock\Lock($redis, JobLock::buildLock($commitPath), array('timeout' => 600000, 'blocking' => false)); if (!$commitLock->acquire()) { Log::info("Job : {$job->getJobId()} Release"); $job->release(30); } $rsyLock = NULL; try { $hosts = (new SiteHost($siteId, $hostType, SiteHost::STATIC_HOST))->getList(); $staticHosts = new SplQueue(); foreach ($hosts as $h) { $staticHosts->push($h); } $hosts = (new SiteHost($siteId, $hostType, SiteHost::WEB_HOST))->getList(); $webHosts = new SplQueue(); foreach ($hosts as $h) { $webHosts->push($h); } /***************************************** * * 执行静态文件同步 * *****************************************/ //执行同步前本地命令 $this->processCommands($staticScript['before']['handle']); while (!$staticHosts->isEmpty()) { $host = $staticHosts->shift(); $rsyLock = new \Eleme\Rlock\Lock($redis, JobLock::rsyLock($host['hostip']), array('timeout' => 600000, 'blocking' => false)); if ($rsyLock->acquire()) { try { $HOST_NAME = $host['hostname']; //执行同步前每次都执行的本地命令 $this->processCommands($staticScript['before']['local']); //执行同步前每次都执行的远端命令 $this->processCommands($staticScript['before']['remote'], $HOST_NAME); Log::info("deploying static files to {$HOST_NAME}."); (new Process($this->remoteProcess($HOST_NAME, "sudo mkdir -p {$REMOTE_STATIC_DIR}")))->mustRun(); (new Process($this->remoteProcess($HOST_NAME, "sudo chown {$remoteUser} -R {$REMOTE_STATIC_DIR}")))->mustRun(); (new Process("rsync -az --progress --force --delay-updates --exclude-from={$RSYNC_EXCLUDE} {$LOCAL_STATIC_DIR}/ {$HOST_NAME}:{$REMOTE_STATIC_DIR}/", $commitPath))->setTimeout(600)->mustRun(); (new Process($this->remoteProcess($HOST_NAME, "sudo chown {$remoteOwner} -R {$REMOTE_STATIC_DIR}")))->mustRun(); //执行同步后每次都执行的本地命令 $this->processCommands($staticScript['after']['local']); //执行同步后每次都执行的远端命令 $this->processCommands($staticScript['after']['remote'], $HOST_NAME); $rsyLock->release(); } catch (Exception $e) { $rsyLock->release(); throw $e; } } else { // 正在同步,重新放回队列 $staticHosts->push($host); } } //执行同步后本地命令 $this->processCommands($staticScript['after']['handle']); /***************************************** * * 执行WEB应用同步 * *****************************************/ //执行同步前本地命令 $this->processCommands($webScript['before']['handle']); while (!$webHosts->isEmpty()) { $host = $webHosts->shift(); $rsyLock = new \Eleme\Rlock\Lock($redis, JobLock::rsyLock($host['hostip']), array('timeout' => 600000, 'blocking' => false)); if ($rsyLock->acquire()) { try { $HOST_NAME = $host['hostname']; //执行同步前每次都执行的本地命令 $this->processCommands($webScript['before']['local']); //执行同步前每次都执行的远端命令 $this->processCommands($webScript['before']['remote'], $HOST_NAME); Log::info("deploying web apps to {$HOST_NAME}."); (new Process($this->remoteProcess($HOST_NAME, "sudo mkdir -p {$REMOTE_DIR}")))->mustRun(); (new Process($this->remoteProcess($HOST_NAME, "sudo chown {$remoteUser} -R {$REMOTE_DIR}")))->mustRun(); (new Process("rsync -azq --progress --force --delete --delay-updates --exclude-from={$RSYNC_EXCLUDE} {$LOCAL_DIR}/ {$HOST_NAME}:{$REMOTE_DIR}/", $commitPath))->setTimeout(600)->mustRun(); (new Process($this->remoteProcess($HOST_NAME, "sudo chown {$remoteOwner} -R {$REMOTE_DIR}")))->mustRun(); //执行同步后每次都执行的本地命令 $this->processCommands($webScript['after']['local']); //执行同步后每次都执行的远端命令 $this->processCommands($webScript['after']['remote'], $HOST_NAME); $rsyLock->release(); } catch (Exception $e) { $rsyLock->release(); throw $e; } } else { $webHosts->push($host); } } //执行同步后本地命令 $this->processCommands($webScript['after']['handle']); $errMsg = ''; foreach ($this->warnings as $w) { $errMsg .= "{$w}\n"; } $errMsg = empty($errMsg) ? null : $errMsg; $this->updateStatus('Deploy Success', $errMsg); Log::info($job->getJobId() . " finish"); } catch (Exception $e) { //if ($rsyLock != null) $rsyLock->release(); $this->updateStatus('Error', "file : " . $e->getFile() . "\nline : " . $e->getLine() . "\n Error Msg : " . $e->getMessage()); Log::error($e->getMessage()); Log::info($job->getJobId() . " Error Finish\n---------------------------"); } $commitLock->release(); $job->delete(); }
/** * @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(); }); }
/** * @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); }); }
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; } }
<?php $queue = new SplQueue(); // errors try { $queue->dequeue(); } catch (RuntimeException $e) { echo "Exception: " . $e->getMessage() . "\n"; } try { $queue->shift(); } catch (RuntimeException $e) { echo "Exception: " . $e->getMessage() . "\n"; } // data consistency $a = 2; $queue->enqueue($a); echo $queue->dequeue() . "\n"; // peakable $queue->enqueue(1); $queue->enqueue(2); echo $queue->top() . "\n"; // iterable foreach ($queue as $elem) { echo "[{$elem}]\n"; } // countable $queue->enqueue(NULL); $queue->enqueue(NULL); echo count($queue) . "\n"; echo $queue->count() . "\n";
/** * @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, $peer, $delayed) = $writeQueue->shift(); $length = strlen($data); if (0 === $length) { $delayed->resolve($previous); } else { $written = stream_socket_sendto($resource, substr($data, 0, self::MAX_PACKET_SIZE), 0, $peer); // Having difficulty finding a test to cover this scenario, but the check seems appropriate. if (false === $written || -1 === $written || 0 === $written) { $message = 'Failed to write to datagram.'; 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, $peer, $delayed]); } } if (!$writeQueue->isEmpty()) { $await->listen(); } }); }
/** * 从开头移出 middleware * * @return callable */ public function shift() { return $this->queue->shift(); }
/** * @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); } }); }