diff --git a/libweb3jsonrpc/UnixSocketServer.cpp b/libweb3jsonrpc/UnixSocketServer.cpp
index 9510f1a6c4d..1ef7b1891b6 100644
--- a/libweb3jsonrpc/UnixSocketServer.cpp
+++ b/libweb3jsonrpc/UnixSocketServer.cpp
@@ -22,6 +22,9 @@ along with cpp-ethereum. If not, see .
#if !defined(_WIN32)
#include "UnixSocketServer.h"
+#include
+#include
+#include
#include
#include
#include
@@ -54,16 +57,45 @@ fs::path getIpcPathOrDataDir()
return getDataDir();
return path;
}
+
+/**
+ * Waits for one of the file descriptors to become readable.
+ * @retval true One file descriptor became available.
+ * @retval false Most likely an error occured, or a timeout.
+ */
+static bool waitForReadable(std::initializer_list sockets)
+{
+ fd_set in, out, err;
+ FD_ZERO(&in);
+ FD_ZERO(&out);
+ FD_ZERO(&err);
+
+ int const maxfd = *std::max_element(sockets.begin(), sockets.end());
+ for (int fd : sockets)
+ FD_SET(fd, &in);
+
+ return select(maxfd + 1, &in, &out, &err, nullptr) > 0;
+}
+
}
UnixDomainSocketServer::UnixDomainSocketServer(string const& _appId):
- IpcServerBase((getIpcPathOrDataDir() / fs::path(_appId + ".ipc")).string().substr(0, c_socketPathMaxLength))
+ IpcServerBase((getIpcPathOrDataDir() / fs::path(_appId + ".ipc")).string().substr(0, c_socketPathMaxLength)),
+ m_exitChannel{-1, -1}
{
+ if (pipe(m_exitChannel) < 0)
+ abort(); // I don't like. Replace me with throw.
+
+ fcntl(m_exitChannel[0], F_SETFL, fcntl(m_exitChannel[0], F_GETFL) | O_NONBLOCK);
+ fcntl(m_exitChannel[1], F_SETFL, fcntl(m_exitChannel[1], F_GETFL) | O_NONBLOCK);
}
UnixDomainSocketServer::~UnixDomainSocketServer()
{
StopListening();
+
+ ::close(m_exitChannel[0]);
+ ::close(m_exitChannel[1]);
}
bool UnixDomainSocketServer::StartListening()
@@ -92,6 +124,10 @@ bool UnixDomainSocketServer::StartListening()
bool UnixDomainSocketServer::StopListening()
{
+ int dummy = 0x90;
+ if (::write(m_exitChannel[1], &dummy, sizeof(dummy)) < 0)
+ cerr << "Failed to notify UNIX domain server loop. " << strerror(errno) << "\n";
+
shutdown(m_socket, SHUT_RDWR);
close(m_socket);
m_socket = -1;
@@ -108,6 +144,13 @@ void UnixDomainSocketServer::Listen()
socklen_t addressLen = sizeof(m_address);
while (m_running)
{
+ // Block until either m_socket is readable or skip tail and recheck for m_running.
+ // We do this test before calling accept() in order to gracefully exit
+ // this function when an external thread has set m_running to true
+ // (such as a signal handler in the main thread).
+ if (!waitForReadable({m_socket, m_exitChannel[0]}))
+ continue;
+
int connection = accept(m_socket, (sockaddr*) &(m_address), &addressLen);
if (connection > 0)
{
diff --git a/libweb3jsonrpc/UnixSocketServer.h b/libweb3jsonrpc/UnixSocketServer.h
index 8fab31d99f0..38d82690f97 100644
--- a/libweb3jsonrpc/UnixSocketServer.h
+++ b/libweb3jsonrpc/UnixSocketServer.h
@@ -38,6 +38,7 @@ class UnixDomainSocketServer : public IpcServerBase
size_t Read(int _connection, void* _data, size_t _size) override;
sockaddr_un m_address;
+ int m_exitChannel[2];
std::atomic m_socket{0};
};