/** * Update one client, processing any commands it has sent us. We fully * process all commands we've received here before returning to the main * server loop. * * @param ArcanistHgClientChannel The client to update. * @param ArcanistHgServerChannel The Mercurial server. * * @task server */ private function updateClient(ArcanistHgClientChannel $client, ArcanistHgServerChannel $hg) { if (!$client->update()) { // Client has disconnected, don't bother proceeding. return false; } // Read a command from the client if one is available. Note that we stop // updating other clients or accepting new connections while processing a // command, since there isn't much we can do with them until the server // finishes executing this command. $message = $client->read(); if (!$message) { return true; } $this->log($client, '$ ' . $message[0] . ' ' . $message[1]); $t_start = microtime(true); // Forward the command to the server. $hg->write($message); while (true) { PhutilChannel::waitForAny(array($client, $hg)); if (!$client->update() || !$hg->update()) { // If either the client or server has exited, bail. return false; } $response = $hg->read(); if (!$response) { continue; } // Forward the response back to the client. $client->write($response); // If the response was on the 'r'esult channel, it indicates the end // of the command output. We can process the next command (if any // remain) or go back to accepting new connections and servicing // other clients. if ($response[0] == 'r') { // Update the client immediately to try to get the bytes on the wire // as quickly as possible. This gives us slightly more throughput. $client->update(); break; } } // Log the elapsed time. $t_end = microtime(true); $t = 1000000 * ($t_end - $t_start); $this->log($client, pht('< %sus', number_format($t, 0))); $this->idleSince = time(); return true; }