diff --git a/application/controllers/AsyncControllerHelper.php b/application/controllers/AsyncControllerHelper.php index 126dcc18..09116f95 100644 --- a/application/controllers/AsyncControllerHelper.php +++ b/application/controllers/AsyncControllerHelper.php @@ -18,10 +18,11 @@ trait AsyncControllerHelper protected $remoteClient; /** - * Call the daemon synchronously and close the RPC connection afterwards + * Call the daemon synchronously * - * The socket is closed in finally so ReactPHP does not keep an active - * stream watcher after the awaited request has completed. + * The RPC connection is shared by all calls made while handling a single + * request and is closed once, on shutdown, so that ReactPHP does not keep + * an active stream watcher after the response has been sent. * * @param string $method * @param mixed[] $params @@ -31,11 +32,7 @@ trait AsyncControllerHelper */ protected function syncRpcCall($method, $params = [], $timeout = 30) { - try { - return await(timeout($this->remoteClient()->request($method, $params), $timeout)); - } finally { - $this->remoteClient()->close(); - } + return await(timeout($this->remoteClient()->request($method, $params), $timeout)); } /** @@ -45,6 +42,11 @@ protected function remoteClient() { if ($this->remoteClient === null) { $this->remoteClient = new RemoteClient(Configuration::getSocketPath(), $this->loop()); + register_shutdown_function(function () { + if ($this->remoteClient !== null) { + $this->remoteClient->close(); + } + }); } return $this->remoteClient; diff --git a/library/Vspheredb/Daemon/RemoteClient.php b/library/Vspheredb/Daemon/RemoteClient.php index 2093101d..9ed1acbe 100644 --- a/library/Vspheredb/Daemon/RemoteClient.php +++ b/library/Vspheredb/Daemon/RemoteClient.php @@ -47,6 +47,10 @@ public function request($method, $params = null) /** * Close the JSON-RPC connection and deregister its event loop watchers * + * The pending connection is reset unconditionally: a failed connect leaves + * $connection null while its rejected promise is still stored, and handing + * that promise to the next call would resolve a connection that is gone. + * * @return void */ public function close(): void @@ -54,6 +58,7 @@ public function close(): void if ($this->connection !== null) { $this->connection->close(); } + $this->pendingConnection = null; } public function notify($method, $params = null)