diff options
author | Max Kellermann <max@duempel.org> | 2014-12-14 15:56:53 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2014-12-14 16:02:47 +0100 |
commit | a543627abd19f321ddd4259e222e6437e8312417 (patch) | |
tree | 9a0494216cc13a0785d972f762c556c0718e3335 | |
parent | 80f2ba7fca533de38575bdaf737ea89284ed2b7b (diff) | |
download | mpd-a543627abd19f321ddd4259e222e6437e8312417.tar.gz mpd-a543627abd19f321ddd4259e222e6437e8312417.tar.xz mpd-a543627abd19f321ddd4259e222e6437e8312417.zip |
lib/nfs/Connection: fix memory leak (and assertion failure)
nfs_destroy_context() will invoke all pending callbacks with
err==-EINTR. In CancellableCallback::Callback(), this will invoke
NfsConnection::DeferClose(), which however is only designed to be
called from nfs_service(). In non-debug mode, this will leak memory
because nfs_close_async() is never called.
Workaround: before nfs_destroy_context(), invoke nfs_close_async() on
all pending file handles.
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | src/lib/nfs/Cancellable.hxx | 6 | ||||
-rw-r--r-- | src/lib/nfs/Connection.cxx | 15 | ||||
-rw-r--r-- | src/lib/nfs/Connection.hxx | 7 |
4 files changed, 29 insertions, 0 deletions
@@ -1,6 +1,7 @@ ver 0.19.7 (not yet released) * input - nfs: fix crash while canceling a failing file open operation + - nfs: fix memory leak on connection failure * playlist - don't skip non-existent songs in "listplaylist" * fix memory allocator bug on Windows diff --git a/src/lib/nfs/Cancellable.hxx b/src/lib/nfs/Cancellable.hxx index be4527ac3..151be0528 100644 --- a/src/lib/nfs/Cancellable.hxx +++ b/src/lib/nfs/Cancellable.hxx @@ -157,6 +157,12 @@ public: return *i; } + + template<typename F> + void ForEach(F &&f) { + for (CT &i : list) + f(i); + } }; #endif diff --git a/src/lib/nfs/Connection.cxx b/src/lib/nfs/Connection.cxx index 48db620be..83b388dc4 100644 --- a/src/lib/nfs/Connection.cxx +++ b/src/lib/nfs/Connection.cxx @@ -133,6 +133,17 @@ NfsConnection::CancellableCallback::CancelAndScheduleClose(struct nfsfh *fh) } inline void +NfsConnection::CancellableCallback::PrepareDestroyContext() +{ + assert(IsCancelled()); + + if (close_fh != nullptr) { + connection.InternalClose(close_fh); + close_fh = nullptr; + } +} + +inline void NfsConnection::CancellableCallback::Callback(int err, void *data) { assert(connection.GetEventLoop().IsInside()); @@ -370,6 +381,10 @@ NfsConnection::DestroyContext() if (SocketMonitor::IsDefined()) SocketMonitor::Cancel(); + callbacks.ForEach([](CancellableCallback &c){ + c.PrepareDestroyContext(); + }); + nfs_destroy_context(context); context = nullptr; } diff --git a/src/lib/nfs/Connection.hxx b/src/lib/nfs/Connection.hxx index 3d872eb3a..e47ba404b 100644 --- a/src/lib/nfs/Connection.hxx +++ b/src/lib/nfs/Connection.hxx @@ -84,6 +84,13 @@ class NfsConnection : SocketMonitor, DeferredMonitor { */ void CancelAndScheduleClose(struct nfsfh *fh); + /** + * Called by NfsConnection::DestroyContext() right + * before nfs_destroy_context(). This object is given + * a chance to prepare for the latter. + */ + void PrepareDestroyContext(); + private: static void Callback(int err, struct nfs_context *nfs, void *data, void *private_data); |