Example #1
0
 /**
  * @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;
 }
Example #3
0
 /**
  * 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;
 }
Example #4
0
 /**
  * {@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;
 }
Example #5
0
 /**
  * @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']);
             }
         }
     }
 }
Example #6
0
 /**
  * {@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);
     }
 }
Example #8
0
 /**
  * @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;
 }
Example #9
0
 /**
  * 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());
             }
         }
     });
 }
Example #10
0
 /**
  * @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();
 }
Example #12
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();
     });
 }
Example #13
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);
     });
 }
Example #14
0
 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;
     }
 }
Example #15
0
<?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";
Example #16
0
 /**
  * @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();
         }
     });
 }
Example #17
0
 /**
  * 从开头移出 middleware
  *
  * @return callable
  */
 public function shift()
 {
     return $this->queue->shift();
 }
Example #18
0
 /**
  * @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);
         }
     });
 }