Merge branch 'v0.19.x'
This commit is contained in:
commit
adfc5db3d2
7
NEWS
7
NEWS
@ -19,6 +19,13 @@ ver 0.20 (not yet released)
|
|||||||
* remove dependency on GLib
|
* remove dependency on GLib
|
||||||
|
|
||||||
ver 0.19.7 (not yet released)
|
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
|
||||||
|
- nfs: fix reconnect after mount failure
|
||||||
|
- nfs: implement mount timeout (60 seconds)
|
||||||
|
* storage
|
||||||
|
- nfs: implement I/O timeout (60 seconds)
|
||||||
* playlist
|
* playlist
|
||||||
- don't skip non-existent songs in "listplaylist"
|
- don't skip non-existent songs in "listplaylist"
|
||||||
* fix memory allocator bug on Windows
|
* fix memory allocator bug on Windows
|
||||||
|
@ -179,9 +179,10 @@ EventLoop::Run()
|
|||||||
mutex.lock();
|
mutex.lock();
|
||||||
HandleDeferred();
|
HandleDeferred();
|
||||||
busy = false;
|
busy = false;
|
||||||
|
const bool _again = again;
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
|
|
||||||
if (again)
|
if (_again)
|
||||||
/* re-evaluate timers because one of the
|
/* re-evaluate timers because one of the
|
||||||
IdleMonitors may have added a new
|
IdleMonitors may have added a new
|
||||||
timeout */
|
timeout */
|
||||||
|
@ -20,7 +20,9 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "Blocking.hxx"
|
#include "Blocking.hxx"
|
||||||
#include "Connection.hxx"
|
#include "Connection.hxx"
|
||||||
|
#include "Domain.hxx"
|
||||||
#include "event/Call.hxx"
|
#include "event/Call.hxx"
|
||||||
|
#include "util/Error.hxx"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BlockingNfsOperation::Run(Error &_error)
|
BlockingNfsOperation::Run(Error &_error)
|
||||||
@ -31,7 +33,10 @@ BlockingNfsOperation::Run(Error &_error)
|
|||||||
[this](){ connection.AddLease(*this); });
|
[this](){ connection.AddLease(*this); });
|
||||||
|
|
||||||
/* wait for completion */
|
/* wait for completion */
|
||||||
LockWaitFinished();
|
if (!LockWaitFinished()) {
|
||||||
|
_error.Set(nfs_domain, 0, "Timeout");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* check for error */
|
/* check for error */
|
||||||
if (error.IsDefined()) {
|
if (error.IsDefined()) {
|
||||||
|
@ -35,6 +35,8 @@ class NfsConnection;
|
|||||||
* thread, and method Run() waits for completion.
|
* thread, and method Run() waits for completion.
|
||||||
*/
|
*/
|
||||||
class BlockingNfsOperation : protected NfsCallback, NfsLease {
|
class BlockingNfsOperation : protected NfsCallback, NfsLease {
|
||||||
|
static constexpr unsigned timeout_ms = 60000;
|
||||||
|
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
Cond cond;
|
Cond cond;
|
||||||
|
|
||||||
@ -52,10 +54,13 @@ public:
|
|||||||
bool Run(Error &error);
|
bool Run(Error &error);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LockWaitFinished() {
|
bool LockWaitFinished() {
|
||||||
const ScopeLock protect(mutex);
|
const ScopeLock protect(mutex);
|
||||||
while (!finished)
|
while (!finished)
|
||||||
cond.wait(mutex);
|
if (!cond.timed_wait(mutex, timeout_ms))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,6 +157,12 @@ public:
|
|||||||
|
|
||||||
return *i;
|
return *i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
void ForEach(F &&f) {
|
||||||
|
for (CT &i : list)
|
||||||
|
f(i);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -34,11 +34,15 @@ extern "C" {
|
|||||||
|
|
||||||
#include <poll.h> /* for POLLIN, POLLOUT */
|
#include <poll.h> /* for POLLIN, POLLOUT */
|
||||||
|
|
||||||
|
static constexpr unsigned NFS_MOUNT_TIMEOUT = 60;
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
NfsConnection::CancellableCallback::Stat(nfs_context *ctx,
|
NfsConnection::CancellableCallback::Stat(nfs_context *ctx,
|
||||||
const char *path,
|
const char *path,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
|
assert(connection.GetEventLoop().IsInside());
|
||||||
|
|
||||||
int result = nfs_stat_async(ctx, path, Callback, this);
|
int result = nfs_stat_async(ctx, path, Callback, this);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
error.Format(nfs_domain, "nfs_stat_async() failed: %s",
|
error.Format(nfs_domain, "nfs_stat_async() failed: %s",
|
||||||
@ -54,6 +58,8 @@ NfsConnection::CancellableCallback::OpenDirectory(nfs_context *ctx,
|
|||||||
const char *path,
|
const char *path,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
|
assert(connection.GetEventLoop().IsInside());
|
||||||
|
|
||||||
int result = nfs_opendir_async(ctx, path, Callback, this);
|
int result = nfs_opendir_async(ctx, path, Callback, this);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
error.Format(nfs_domain, "nfs_opendir_async() failed: %s",
|
error.Format(nfs_domain, "nfs_opendir_async() failed: %s",
|
||||||
@ -69,6 +75,8 @@ NfsConnection::CancellableCallback::Open(nfs_context *ctx,
|
|||||||
const char *path, int flags,
|
const char *path, int flags,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
|
assert(connection.GetEventLoop().IsInside());
|
||||||
|
|
||||||
int result = nfs_open_async(ctx, path, flags,
|
int result = nfs_open_async(ctx, path, flags,
|
||||||
Callback, this);
|
Callback, this);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
@ -85,6 +93,8 @@ NfsConnection::CancellableCallback::Stat(nfs_context *ctx,
|
|||||||
struct nfsfh *fh,
|
struct nfsfh *fh,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
|
assert(connection.GetEventLoop().IsInside());
|
||||||
|
|
||||||
int result = nfs_fstat_async(ctx, fh, Callback, this);
|
int result = nfs_fstat_async(ctx, fh, Callback, this);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
error.Format(nfs_domain, "nfs_fstat_async() failed: %s",
|
error.Format(nfs_domain, "nfs_fstat_async() failed: %s",
|
||||||
@ -100,6 +110,8 @@ NfsConnection::CancellableCallback::Read(nfs_context *ctx, struct nfsfh *fh,
|
|||||||
uint64_t offset, size_t size,
|
uint64_t offset, size_t size,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
|
assert(connection.GetEventLoop().IsInside());
|
||||||
|
|
||||||
int result = nfs_pread_async(ctx, fh, offset, size, Callback, this);
|
int result = nfs_pread_async(ctx, fh, offset, size, Callback, this);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
error.Format(nfs_domain, "nfs_pread_async() failed: %s",
|
error.Format(nfs_domain, "nfs_pread_async() failed: %s",
|
||||||
@ -113,6 +125,7 @@ NfsConnection::CancellableCallback::Read(nfs_context *ctx, struct nfsfh *fh,
|
|||||||
inline void
|
inline void
|
||||||
NfsConnection::CancellableCallback::CancelAndScheduleClose(struct nfsfh *fh)
|
NfsConnection::CancellableCallback::CancelAndScheduleClose(struct nfsfh *fh)
|
||||||
{
|
{
|
||||||
|
assert(connection.GetEventLoop().IsInside());
|
||||||
assert(!open);
|
assert(!open);
|
||||||
assert(close_fh == nullptr);
|
assert(close_fh == nullptr);
|
||||||
assert(fh != nullptr);
|
assert(fh != nullptr);
|
||||||
@ -121,9 +134,22 @@ NfsConnection::CancellableCallback::CancelAndScheduleClose(struct nfsfh *fh)
|
|||||||
Cancel();
|
Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
NfsConnection::CancellableCallback::PrepareDestroyContext()
|
||||||
|
{
|
||||||
|
assert(IsCancelled());
|
||||||
|
|
||||||
|
if (close_fh != nullptr) {
|
||||||
|
connection.InternalClose(close_fh);
|
||||||
|
close_fh = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
NfsConnection::CancellableCallback::Callback(int err, void *data)
|
NfsConnection::CancellableCallback::Callback(int err, void *data)
|
||||||
{
|
{
|
||||||
|
assert(connection.GetEventLoop().IsInside());
|
||||||
|
|
||||||
if (!IsCancelled()) {
|
if (!IsCancelled()) {
|
||||||
assert(close_fh == nullptr);
|
assert(close_fh == nullptr);
|
||||||
|
|
||||||
@ -143,8 +169,10 @@ NfsConnection::CancellableCallback::Callback(int err, void *data)
|
|||||||
allocated file handle immediately */
|
allocated file handle immediately */
|
||||||
assert(close_fh == nullptr);
|
assert(close_fh == nullptr);
|
||||||
|
|
||||||
struct nfsfh *fh = (struct nfsfh *)data;
|
if (err >= 0) {
|
||||||
connection.Close(fh);
|
struct nfsfh *fh = (struct nfsfh *)data;
|
||||||
|
connection.Close(fh);
|
||||||
|
}
|
||||||
} else if (close_fh != nullptr)
|
} else if (close_fh != nullptr)
|
||||||
connection.DeferClose(close_fh);
|
connection.DeferClose(close_fh);
|
||||||
|
|
||||||
@ -209,6 +237,7 @@ NfsConnection::RemoveLease(NfsLease &lease)
|
|||||||
bool
|
bool
|
||||||
NfsConnection::Stat(const char *path, NfsCallback &callback, Error &error)
|
NfsConnection::Stat(const char *path, NfsCallback &callback, Error &error)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
assert(!callbacks.Contains(callback));
|
assert(!callbacks.Contains(callback));
|
||||||
|
|
||||||
auto &c = callbacks.Add(callback, *this, false);
|
auto &c = callbacks.Add(callback, *this, false);
|
||||||
@ -225,6 +254,7 @@ bool
|
|||||||
NfsConnection::OpenDirectory(const char *path, NfsCallback &callback,
|
NfsConnection::OpenDirectory(const char *path, NfsCallback &callback,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
assert(!callbacks.Contains(callback));
|
assert(!callbacks.Contains(callback));
|
||||||
|
|
||||||
auto &c = callbacks.Add(callback, *this, true);
|
auto &c = callbacks.Add(callback, *this, true);
|
||||||
@ -240,12 +270,16 @@ NfsConnection::OpenDirectory(const char *path, NfsCallback &callback,
|
|||||||
const struct nfsdirent *
|
const struct nfsdirent *
|
||||||
NfsConnection::ReadDirectory(struct nfsdir *dir)
|
NfsConnection::ReadDirectory(struct nfsdir *dir)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
|
|
||||||
return nfs_readdir(context, dir);
|
return nfs_readdir(context, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NfsConnection::CloseDirectory(struct nfsdir *dir)
|
NfsConnection::CloseDirectory(struct nfsdir *dir)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
|
|
||||||
return nfs_closedir(context, dir);
|
return nfs_closedir(context, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,6 +287,7 @@ bool
|
|||||||
NfsConnection::Open(const char *path, int flags, NfsCallback &callback,
|
NfsConnection::Open(const char *path, int flags, NfsCallback &callback,
|
||||||
Error &error)
|
Error &error)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
assert(!callbacks.Contains(callback));
|
assert(!callbacks.Contains(callback));
|
||||||
|
|
||||||
auto &c = callbacks.Add(callback, *this, true);
|
auto &c = callbacks.Add(callback, *this, true);
|
||||||
@ -268,6 +303,7 @@ NfsConnection::Open(const char *path, int flags, NfsCallback &callback,
|
|||||||
bool
|
bool
|
||||||
NfsConnection::Stat(struct nfsfh *fh, NfsCallback &callback, Error &error)
|
NfsConnection::Stat(struct nfsfh *fh, NfsCallback &callback, Error &error)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
assert(!callbacks.Contains(callback));
|
assert(!callbacks.Contains(callback));
|
||||||
|
|
||||||
auto &c = callbacks.Add(callback, *this, false);
|
auto &c = callbacks.Add(callback, *this, false);
|
||||||
@ -284,6 +320,7 @@ bool
|
|||||||
NfsConnection::Read(struct nfsfh *fh, uint64_t offset, size_t size,
|
NfsConnection::Read(struct nfsfh *fh, uint64_t offset, size_t size,
|
||||||
NfsCallback &callback, Error &error)
|
NfsCallback &callback, Error &error)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
assert(!callbacks.Contains(callback));
|
assert(!callbacks.Contains(callback));
|
||||||
|
|
||||||
auto &c = callbacks.Add(callback, *this, false);
|
auto &c = callbacks.Add(callback, *this, false);
|
||||||
@ -307,10 +344,22 @@ DummyCallback(int, struct nfs_context *, void *, void *)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
NfsConnection::InternalClose(struct nfsfh *fh)
|
||||||
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
|
assert(context != nullptr);
|
||||||
|
assert(fh != nullptr);
|
||||||
|
|
||||||
|
nfs_close_async(context, fh, DummyCallback, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NfsConnection::Close(struct nfsfh *fh)
|
NfsConnection::Close(struct nfsfh *fh)
|
||||||
{
|
{
|
||||||
nfs_close_async(context, fh, DummyCallback, nullptr);
|
assert(GetEventLoop().IsInside());
|
||||||
|
|
||||||
|
InternalClose(fh);
|
||||||
ScheduleSocket();
|
ScheduleSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,12 +376,26 @@ NfsConnection::DestroyContext()
|
|||||||
assert(GetEventLoop().IsInside());
|
assert(GetEventLoop().IsInside());
|
||||||
assert(context != nullptr);
|
assert(context != nullptr);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(!in_destroy);
|
||||||
|
in_destroy = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!mount_finished) {
|
||||||
|
assert(TimeoutMonitor::IsActive());
|
||||||
|
TimeoutMonitor::Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
/* cancel pending DeferredMonitor that was scheduled to notify
|
/* cancel pending DeferredMonitor that was scheduled to notify
|
||||||
new leases */
|
new leases */
|
||||||
DeferredMonitor::Cancel();
|
DeferredMonitor::Cancel();
|
||||||
|
|
||||||
if (SocketMonitor::IsDefined())
|
if (SocketMonitor::IsDefined())
|
||||||
SocketMonitor::Cancel();
|
SocketMonitor::Steal();
|
||||||
|
|
||||||
|
callbacks.ForEach([](CancellableCallback &c){
|
||||||
|
c.PrepareDestroyContext();
|
||||||
|
});
|
||||||
|
|
||||||
nfs_destroy_context(context);
|
nfs_destroy_context(context);
|
||||||
context = nullptr;
|
context = nullptr;
|
||||||
@ -341,8 +404,11 @@ NfsConnection::DestroyContext()
|
|||||||
inline void
|
inline void
|
||||||
NfsConnection::DeferClose(struct nfsfh *fh)
|
NfsConnection::DeferClose(struct nfsfh *fh)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
assert(in_event);
|
assert(in_event);
|
||||||
assert(in_service);
|
assert(in_service);
|
||||||
|
assert(context != nullptr);
|
||||||
|
assert(fh != nullptr);
|
||||||
|
|
||||||
deferred_close.push_front(fh);
|
deferred_close.push_front(fh);
|
||||||
}
|
}
|
||||||
@ -350,6 +416,7 @@ NfsConnection::DeferClose(struct nfsfh *fh)
|
|||||||
void
|
void
|
||||||
NfsConnection::ScheduleSocket()
|
NfsConnection::ScheduleSocket()
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
assert(context != nullptr);
|
assert(context != nullptr);
|
||||||
|
|
||||||
if (!SocketMonitor::IsDefined()) {
|
if (!SocketMonitor::IsDefined()) {
|
||||||
@ -364,9 +431,35 @@ NfsConnection::ScheduleSocket()
|
|||||||
SocketMonitor::Schedule(libnfs_to_events(nfs_which_events(context)));
|
SocketMonitor::Schedule(libnfs_to_events(nfs_which_events(context)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int
|
||||||
|
NfsConnection::Service(unsigned flags)
|
||||||
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
|
assert(context != nullptr);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(!in_event);
|
||||||
|
in_event = true;
|
||||||
|
|
||||||
|
assert(!in_service);
|
||||||
|
in_service = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int result = nfs_service(context, events_to_libnfs(flags));
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(context != nullptr);
|
||||||
|
assert(in_service);
|
||||||
|
in_service = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
NfsConnection::OnSocketReady(unsigned flags)
|
NfsConnection::OnSocketReady(unsigned flags)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
assert(deferred_close.empty());
|
assert(deferred_close.empty());
|
||||||
|
|
||||||
bool closed = false;
|
bool closed = false;
|
||||||
@ -378,21 +471,10 @@ NfsConnection::OnSocketReady(unsigned flags)
|
|||||||
re-register it each time */
|
re-register it each time */
|
||||||
SocketMonitor::Steal();
|
SocketMonitor::Steal();
|
||||||
|
|
||||||
assert(!in_event);
|
const int result = Service(flags);
|
||||||
in_event = true;
|
|
||||||
|
|
||||||
assert(!in_service);
|
|
||||||
in_service = true;
|
|
||||||
|
|
||||||
int result = nfs_service(context, events_to_libnfs(flags));
|
|
||||||
|
|
||||||
assert(context != nullptr);
|
|
||||||
assert(in_service);
|
|
||||||
in_service = false;
|
|
||||||
|
|
||||||
while (!deferred_close.empty()) {
|
while (!deferred_close.empty()) {
|
||||||
nfs_close_async(context, deferred_close.front(),
|
InternalClose(deferred_close.front());
|
||||||
DummyCallback, nullptr);
|
|
||||||
deferred_close.pop_front();
|
deferred_close.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,9 +495,9 @@ NfsConnection::OnSocketReady(unsigned flags)
|
|||||||
|
|
||||||
DestroyContext();
|
DestroyContext();
|
||||||
closed = true;
|
closed = true;
|
||||||
} else if (SocketMonitor::IsDefined() && nfs_get_fd(context) < 0) {
|
} else if (nfs_get_fd(context) < 0) {
|
||||||
/* this happens when rpc_reconnect_requeue() is called
|
/* this happens when rpc_reconnect_requeue() is called
|
||||||
after the connection broke, but autoreconnet was
|
after the connection broke, but autoreconnect was
|
||||||
disabled - nfs_service() returns 0 */
|
disabled - nfs_service() returns 0 */
|
||||||
Error error;
|
Error error;
|
||||||
const char *msg = nfs_get_error(context);
|
const char *msg = nfs_get_error(context);
|
||||||
@ -431,8 +513,12 @@ NfsConnection::OnSocketReady(unsigned flags)
|
|||||||
closed = true;
|
closed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(context == nullptr || nfs_get_fd(context) >= 0);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
assert(in_event);
|
assert(in_event);
|
||||||
in_event = false;
|
in_event = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (context != nullptr)
|
if (context != nullptr)
|
||||||
ScheduleSocket();
|
ScheduleSocket();
|
||||||
@ -444,10 +530,14 @@ inline void
|
|||||||
NfsConnection::MountCallback(int status, gcc_unused nfs_context *nfs,
|
NfsConnection::MountCallback(int status, gcc_unused nfs_context *nfs,
|
||||||
gcc_unused void *data)
|
gcc_unused void *data)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
assert(context == nfs);
|
assert(context == nfs);
|
||||||
|
|
||||||
mount_finished = true;
|
mount_finished = true;
|
||||||
|
|
||||||
|
assert(TimeoutMonitor::IsActive() || in_destroy);
|
||||||
|
TimeoutMonitor::Cancel();
|
||||||
|
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
postponed_mount_error.Format(nfs_domain, status,
|
postponed_mount_error.Format(nfs_domain, status,
|
||||||
"nfs_mount_async() failed: %s",
|
"nfs_mount_async() failed: %s",
|
||||||
@ -468,6 +558,7 @@ NfsConnection::MountCallback(int status, nfs_context *nfs, void *data,
|
|||||||
inline bool
|
inline bool
|
||||||
NfsConnection::MountInternal(Error &error)
|
NfsConnection::MountInternal(Error &error)
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
assert(context == nullptr);
|
assert(context == nullptr);
|
||||||
|
|
||||||
context = nfs_init_context();
|
context = nfs_init_context();
|
||||||
@ -478,8 +569,14 @@ NfsConnection::MountInternal(Error &error)
|
|||||||
|
|
||||||
postponed_mount_error.Clear();
|
postponed_mount_error.Clear();
|
||||||
mount_finished = false;
|
mount_finished = false;
|
||||||
|
|
||||||
|
TimeoutMonitor::ScheduleSeconds(NFS_MOUNT_TIMEOUT);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
in_service = false;
|
in_service = false;
|
||||||
in_event = false;
|
in_event = false;
|
||||||
|
in_destroy = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (nfs_mount_async(context, server.c_str(), export_name.c_str(),
|
if (nfs_mount_async(context, server.c_str(), export_name.c_str(),
|
||||||
MountCallback, this) != 0) {
|
MountCallback, this) != 0) {
|
||||||
@ -535,9 +632,23 @@ NfsConnection::BroadcastError(Error &&error)
|
|||||||
BroadcastMountError(std::move(error));
|
BroadcastMountError(std::move(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NfsConnection::OnTimeout()
|
||||||
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
|
assert(!mount_finished);
|
||||||
|
|
||||||
|
mount_finished = true;
|
||||||
|
DestroyContext();
|
||||||
|
|
||||||
|
BroadcastMountError(Error(nfs_domain, "Mount timeout"));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NfsConnection::RunDeferred()
|
NfsConnection::RunDeferred()
|
||||||
{
|
{
|
||||||
|
assert(GetEventLoop().IsInside());
|
||||||
|
|
||||||
if (context == nullptr) {
|
if (context == nullptr) {
|
||||||
Error error;
|
Error error;
|
||||||
if (!MountInternal(error)) {
|
if (!MountInternal(error)) {
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "Lease.hxx"
|
#include "Lease.hxx"
|
||||||
#include "Cancellable.hxx"
|
#include "Cancellable.hxx"
|
||||||
#include "event/SocketMonitor.hxx"
|
#include "event/SocketMonitor.hxx"
|
||||||
|
#include "event/TimeoutMonitor.hxx"
|
||||||
#include "event/DeferredMonitor.hxx"
|
#include "event/DeferredMonitor.hxx"
|
||||||
#include "util/Error.hxx"
|
#include "util/Error.hxx"
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ class NfsCallback;
|
|||||||
/**
|
/**
|
||||||
* An asynchronous connection to a NFS server.
|
* An asynchronous connection to a NFS server.
|
||||||
*/
|
*/
|
||||||
class NfsConnection : SocketMonitor, DeferredMonitor {
|
class NfsConnection : SocketMonitor, TimeoutMonitor, DeferredMonitor {
|
||||||
class CancellableCallback : public CancellablePointer<NfsCallback> {
|
class CancellableCallback : public CancellablePointer<NfsCallback> {
|
||||||
NfsConnection &connection;
|
NfsConnection &connection;
|
||||||
|
|
||||||
@ -84,6 +85,13 @@ class NfsConnection : SocketMonitor, DeferredMonitor {
|
|||||||
*/
|
*/
|
||||||
void CancelAndScheduleClose(struct nfsfh *fh);
|
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:
|
private:
|
||||||
static void Callback(int err, struct nfs_context *nfs,
|
static void Callback(int err, struct nfs_context *nfs,
|
||||||
void *data, void *private_data);
|
void *data, void *private_data);
|
||||||
@ -111,6 +119,7 @@ class NfsConnection : SocketMonitor, DeferredMonitor {
|
|||||||
|
|
||||||
Error postponed_mount_error;
|
Error postponed_mount_error;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
/**
|
/**
|
||||||
* True when nfs_service() is being called.
|
* True when nfs_service() is being called.
|
||||||
*/
|
*/
|
||||||
@ -122,13 +131,20 @@ class NfsConnection : SocketMonitor, DeferredMonitor {
|
|||||||
*/
|
*/
|
||||||
bool in_event;
|
bool in_event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True when DestroyContext() is being called.
|
||||||
|
*/
|
||||||
|
bool in_destroy;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool mount_finished;
|
bool mount_finished;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
gcc_nonnull_all
|
gcc_nonnull_all
|
||||||
NfsConnection(EventLoop &_loop,
|
NfsConnection(EventLoop &_loop,
|
||||||
const char *_server, const char *_export_name)
|
const char *_server, const char *_export_name)
|
||||||
:SocketMonitor(_loop), DeferredMonitor(_loop),
|
:SocketMonitor(_loop), TimeoutMonitor(_loop),
|
||||||
|
DeferredMonitor(_loop),
|
||||||
server(_server), export_name(_export_name),
|
server(_server), export_name(_export_name),
|
||||||
context(nullptr) {}
|
context(nullptr) {}
|
||||||
|
|
||||||
@ -184,6 +200,11 @@ protected:
|
|||||||
private:
|
private:
|
||||||
void DestroyContext();
|
void DestroyContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for nfs_close_async().
|
||||||
|
*/
|
||||||
|
void InternalClose(struct nfsfh *fh);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke nfs_close_async() after nfs_service() returns.
|
* Invoke nfs_close_async() after nfs_service() returns.
|
||||||
*/
|
*/
|
||||||
@ -200,9 +221,17 @@ private:
|
|||||||
|
|
||||||
void ScheduleSocket();
|
void ScheduleSocket();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for nfs_service().
|
||||||
|
*/
|
||||||
|
int Service(unsigned flags);
|
||||||
|
|
||||||
/* virtual methods from SocketMonitor */
|
/* virtual methods from SocketMonitor */
|
||||||
virtual bool OnSocketReady(unsigned flags) override;
|
virtual bool OnSocketReady(unsigned flags) override;
|
||||||
|
|
||||||
|
/* virtual methods from TimeoutMonitor */
|
||||||
|
void OnTimeout() final;
|
||||||
|
|
||||||
/* virtual methods from DeferredMonitor */
|
/* virtual methods from DeferredMonitor */
|
||||||
virtual void RunDeferred() override;
|
virtual void RunDeferred() override;
|
||||||
};
|
};
|
||||||
|
@ -146,6 +146,7 @@ private:
|
|||||||
|
|
||||||
const ScopeLock protect(mutex);
|
const ScopeLock protect(mutex);
|
||||||
state = _state;
|
state = _state;
|
||||||
|
last_error.Clear();
|
||||||
last_error.Set(error);
|
last_error.Set(error);
|
||||||
cond.broadcast();
|
cond.broadcast();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user