Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions application/controllers/AsyncControllerHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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));
}

/**
Expand All @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions library/Vspheredb/Daemon/RemoteClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,18 @@ 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
{
if ($this->connection !== null) {
$this->connection->close();
}
$this->pendingConnection = null;
}

public function notify($method, $params = null)
Expand Down