From 05eac20ffe5af325ac7d4019e72d3ac0b69d494a Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@musicpd.org>
Date: Wed, 1 Feb 2017 21:16:50 +0100
Subject: [PATCH] lib/nfs/Connection: detect libnfs reconnect

When rpc_reconnect_requeue() gets called from inside nfs_service(),
the NfsInputStream can stall completely because the old socket has
been unregistered from epoll automatically, but the new one has never
been registered.  Therefore, nfs_service() will never be called again.

This kludge attempts to detect this condition by checking
nfs_which_events()==POLLOUT.

https://bugs.musicpd.org/view.php?id=4081
---
 NEWS                       |  2 ++
 src/lib/nfs/Connection.cxx | 13 ++++++++++++-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 01c190ac9..d32a375c5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,6 @@
 ver 0.20.4 (not yet released)
+* input
+  - nfs: fix freeze after reconnect
 * output
   - sndio: work around a libroar C++ incompatibility
 * workaround for GCC 4.9 "constexpr" bug
diff --git a/src/lib/nfs/Connection.cxx b/src/lib/nfs/Connection.cxx
index 16f6c001d..e41c4acd8 100644
--- a/src/lib/nfs/Connection.cxx
+++ b/src/lib/nfs/Connection.cxx
@@ -396,6 +396,17 @@ NfsConnection::ScheduleSocket()
 	assert(GetEventLoop().IsInside());
 	assert(context != nullptr);
 
+	const int which_events = nfs_which_events(context);
+
+	if (which_events == POLLOUT && SocketMonitor::IsDefined())
+		/* kludge: if libnfs asks only for POLLOUT, it means
+		   that it is currently waiting for the connect() to
+		   finish - rpc_reconnect_requeue() may have been
+		   called from inside nfs_service(); we must now
+		   unregister the old socket and register the new one
+		   instead */
+		SocketMonitor::Steal();
+
 	if (!SocketMonitor::IsDefined()) {
 		int _fd = nfs_get_fd(context);
 		if (_fd < 0)
@@ -405,7 +416,7 @@ NfsConnection::ScheduleSocket()
 		SocketMonitor::Open(_fd);
 	}
 
-	SocketMonitor::Schedule(libnfs_to_events(nfs_which_events(context)));
+	SocketMonitor::Schedule(libnfs_to_events(which_events));
 }
 
 inline int