---
Notes:
ACKed
Version 4:
- no changes
Version 3:
- no changes
Version 2:
- no changes
src/rpc/virnetclient.c | 76 ++--
1 files changed, 67 insertions(+), 9 deletions(-)
diff --git a/src/rpc/virnetclient.c b/src/rpc/virnetclient.c
index 58ba66d..aaf072a 100644
--- a/src/rpc/virnetclient.c
+++ b/src/rpc/virnetclient.c
@@ -95,9 +95,13 @@ struct _virNetClient {
size_t nstreams;
virNetClientStreamPtr *streams;
+
+bool wantClose;
};
+void virNetClientRequestClose(virNetClientPtr client);
+
static int virNetClientSendInternal(virNetClientPtr client,
virNetMessagePtr msg,
bool expectReply,
@@ -307,12 +311,14 @@ void virNetClientFree(virNetClientPtr client)
}
-void virNetClientClose(virNetClientPtr client)
+static void
+virNetClientCloseLocked(virNetClientPtr client)
{
-if (!client)
+VIR_DEBUG("client=%p, sock=%p", client, client->sock);
+
+if (!client->sock)
return;
-virNetClientLock(client);
virNetSocketRemoveIOCallback(client->sock);
virNetSocketFree(client->sock);
client->sock = NULL;
@@ -322,6 +328,41 @@ void virNetClientClose(virNetClientPtr client)
virNetSASLSessionFree(client->sasl);
client->sasl = NULL;
#endif
+client->wantClose = false;
+}
+
+void virNetClientClose(virNetClientPtr client)
+{
+if (!client)
+return;
+
+virNetClientLock(client);
+virNetClientCloseLocked(client);
+virNetClientUnlock(client);
+}
+
+void
+virNetClientRequestClose(virNetClientPtr client)
+{
+VIR_DEBUG("client=%p", client);
+
+virNetClientLock(client);
+
+/* If there is a thread polling for data on the socket, set wantClose flag
+ * and wake the thread up or just immediately close the socket when no-one
+ * is polling on it.
+ */
+if (client->waitDispatch) {
+char ignore = 1;
+int len = sizeof(ignore);
+
+client->wantClose = true;
+if (safewrite(client->wakeupSendFD, &ignore, len) != len)
+VIR_ERROR(_("failed to wake up polling thread"));
+} else {
+virNetClientCloseLocked(client);
+}
+
virNetClientUnlock(client);
}
@@ -978,11 +1019,12 @@ static int virNetClientIOEventLoop(virNetClientPtr
client,
sigset_t oldmask, blockedsigs;
int timeout = -1;
-/* If we have existing SASL decoded data we
- * don't want to sleep in the poll(), just
- * check if any other FDs are also ready
+/* If we have existing SASL decoded data we don't want to sleep in
+ * the poll(), just check if any other FDs are also ready.
+ * If the connection is going to be closed, we don't want to sleep in
+ * poll() either.
*/
-if (virNetSocketHasCachedData(client->sock))
+if (virNetSocketHasCachedData(client->sock) || client->wantClose)
timeout = 0;
fds[0].events = fds[0].revents = 0;
@@ -1047,6 +1089,11 @@ static int virNetClientIOEventLoop(virNetClientPtr
client,
if (virNetSocketHasCachedData(client->sock))
fds[0].revents |= POLLIN;
+/* If wantClose flag is set, pretend there was an error on the socket
+ */
+if (client->wantClose)
+fds[0].revents = POLLERR;
+
if (fds[1].revents) {
VIR_DEBUG("Woken up from poll by other thread");
if (saferead(client->wakeupReadFD, &ignore, sizeof(ignore)) !=
sizeof(ignore)) {
@@ -1140,6 +1187,8 @@ pass:
if (client->waitDispatch) {
VIR_DEBUG("Passing the buck to %p", client->waitDispatch);
virCondSignal(&client->waitDispatch->cond);
+} else if (client->wantClose) {
+virNetClientCloseLocked(client);
}
return ret;
}
@@ -1281,7 +1330,8 @@ static int virNetClientIO(virNetClientPtr client,
virResetLastError();
rv = virNetClientIOEventLoop(client, thiscall);
-virNetSocketUpdateIOCallback(client->sock, VIR_EVENT_HANDLE_READABLE);
+if (client->sock)
+virNetSocketUpdateIOCallback(client->sock, VIR_EVENT_HANDLE_READABLE);
if (rv == 0 &&
virGetLastError())
@@ -1305,7 +1355,7 @@ void virNetClientIncomingEvent(virNetSocketPtr sock,
goto done;
/* This should be impossible, but it doesn't hurt to check */
-if (client->waitDispatch)
+if (client->waitDispatch || client->wantClose)
goto done;
VIR_DEBUG("Event fired %p %d", sock, events);
@@ -1357,6 +1407,12 @@ virNetClientSendInternal(virNetClientPtr client,
virNetClientLock(client);
+if (!client->sock || client->wantClose) {
+virNetError(VIR_ERR_INTERNAL_ERROR, "%s",
+_("Client socket is closed"));
+goto unlock;
+}
+
/* We don't need call->cond for non-blocking calls since there's no
* thread to be woken up anyway
*/
@@