Index: src/channel/PhutilChannel.php =================================================================== --- src/channel/PhutilChannel.php +++ src/channel/PhutilChannel.php @@ -131,7 +131,13 @@ // busy wait). if (!$r_sockets && !$w_sockets) { - return; + return false; + } + + if ($channel->isWriteBufferEmpty()) { + // If the channel's write buffer is empty, don't select the write + // sockets, since they're writable immediately. + $w_sockets = array(); } foreach ($r_sockets as $socket) { Index: src/channel/PhutilExecChannel.php =================================================================== --- src/channel/PhutilExecChannel.php +++ src/channel/PhutilExecChannel.php @@ -124,6 +124,9 @@ return $this->future->getWriteSockets(); } + protected function isWriteBufferEmpty() { + return $this->future->isWriteBufferEmpty(); + } /** * If the wrapped @{class:ExecFuture} outputs data to stderr, we normally Index: src/channel/PhutilSocketChannel.php =================================================================== --- src/channel/PhutilSocketChannel.php +++ src/channel/PhutilSocketChannel.php @@ -148,9 +148,7 @@ } protected function getWriteSockets() { - if ($this->isWriteBufferEmpty()) { - return array(); - } else if ($this->writeSocket) { + if ($this->writeSocket) { return array($this->writeSocket); } else { return array(); Index: src/future/exec/ExecFuture.php =================================================================== --- src/future/exec/ExecFuture.php +++ src/future/exec/ExecFuture.php @@ -464,6 +464,17 @@ /** + * Determine if the write buffer is empty. + * + * @return bool True if the write buffer is empty. + * @task internal + */ + public function isWriteBufferEmpty() { + return !($this->stdin && $this->stdin->getByteLength()); + } + + + /** * Reads some bytes from a stream, discarding output once a certain amount * has been accumulated. *