From 05eac20ffe5af325ac7d4019e72d3ac0b69d494a Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 1 Feb 2017 21:16:50 +0100 Subject: 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 --- src/lib/nfs/Connection.cxx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/lib/nfs') 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 -- cgit v1.2.3 From 591afa06474b7d71ddb1bf0b95e99387b3378c4c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 1 Feb 2017 21:36:32 +0100 Subject: lib/nfs/Connection: detect socket hangup and unregister from epoll Fixes race condition when epoll_ctl() gets called after the socket has been closed, which may affect a different socket created by another thread meanwhile. --- src/lib/nfs/Connection.cxx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/lib/nfs') diff --git a/src/lib/nfs/Connection.cxx b/src/lib/nfs/Connection.cxx index e41c4acd8..b417f2fab 100644 --- a/src/lib/nfs/Connection.cxx +++ b/src/lib/nfs/Connection.cxx @@ -416,7 +416,8 @@ NfsConnection::ScheduleSocket() SocketMonitor::Open(_fd); } - SocketMonitor::Schedule(libnfs_to_events(which_events)); + SocketMonitor::Schedule(libnfs_to_events(which_events) + | SocketMonitor::HANGUP); } inline int @@ -453,10 +454,14 @@ NfsConnection::OnSocketReady(unsigned flags) bool closed = false; const bool was_mounted = mount_finished; - if (!mount_finished) + if (!mount_finished || (flags & SocketMonitor::HANGUP) != 0) /* until the mount is finished, the NFS client may use various sockets, therefore we unregister and re-register it each time */ + /* also re-register the socket if we got a HANGUP, + which is a sure sign that libnfs will close the + socket, which can lead to a race condition if + epoll_ctl() is called later */ SocketMonitor::Steal(); const int result = Service(flags); -- cgit v1.2.3