{input,storage}/nfs: use C++ exceptions instead of class Error

This commit is contained in:
Max Kellermann 2016-09-16 16:55:57 +02:00
parent 553365b942
commit 539c0ed171
15 changed files with 213 additions and 311 deletions

View File

@ -671,8 +671,7 @@ NFS_SOURCES = \
src/lib/nfs/Glue.cxx src/lib/nfs/Glue.hxx \ src/lib/nfs/Glue.cxx src/lib/nfs/Glue.hxx \
src/lib/nfs/Base.cxx src/lib/nfs/Base.hxx \ src/lib/nfs/Base.cxx src/lib/nfs/Base.hxx \
src/lib/nfs/FileReader.cxx src/lib/nfs/FileReader.hxx \ src/lib/nfs/FileReader.cxx src/lib/nfs/FileReader.hxx \
src/lib/nfs/Blocking.cxx src/lib/nfs/Blocking.hxx \ src/lib/nfs/Blocking.cxx src/lib/nfs/Blocking.hxx
src/lib/nfs/Domain.cxx src/lib/nfs/Domain.hxx
if ENABLE_DATABASE if ENABLE_DATABASE

View File

@ -21,7 +21,6 @@
#include "NfsInputPlugin.hxx" #include "NfsInputPlugin.hxx"
#include "../AsyncInputStream.hxx" #include "../AsyncInputStream.hxx"
#include "../InputPlugin.hxx" #include "../InputPlugin.hxx"
#include "lib/nfs/Domain.hxx"
#include "lib/nfs/Glue.hxx" #include "lib/nfs/Glue.hxx"
#include "lib/nfs/FileReader.hxx" #include "lib/nfs/FileReader.hxx"
#include "util/StringCompare.hxx" #include "util/StringCompare.hxx"
@ -64,7 +63,7 @@ public:
} }
private: private:
bool DoRead(); void DoRead();
protected: protected:
/* virtual methods from AsyncInputStream */ /* virtual methods from AsyncInputStream */
@ -75,41 +74,34 @@ private:
/* virtual methods from NfsFileReader */ /* virtual methods from NfsFileReader */
void OnNfsFileOpen(uint64_t size) override; void OnNfsFileOpen(uint64_t size) override;
void OnNfsFileRead(const void *data, size_t size) override; void OnNfsFileRead(const void *data, size_t size) override;
void OnNfsFileError(Error &&error) override; void OnNfsFileError(std::exception_ptr &&e) override;
}; };
bool void
NfsInputStream::DoRead() NfsInputStream::DoRead()
{ {
assert(NfsFileReader::IsIdle()); assert(NfsFileReader::IsIdle());
int64_t remaining = size - next_offset; int64_t remaining = size - next_offset;
if (remaining <= 0) if (remaining <= 0)
return true; return;
const size_t buffer_space = GetBufferSpace(); const size_t buffer_space = GetBufferSpace();
if (buffer_space == 0) { if (buffer_space == 0) {
Pause(); Pause();
return true; return;
} }
size_t nbytes = std::min<size_t>(std::min<uint64_t>(remaining, 32768), size_t nbytes = std::min<size_t>(std::min<uint64_t>(remaining, 32768),
buffer_space); buffer_space);
Error error; try {
bool success;
{
const ScopeUnlock unlock(mutex); const ScopeUnlock unlock(mutex);
success = NfsFileReader::Read(next_offset, nbytes, error); NfsFileReader::Read(next_offset, nbytes);
} catch (...) {
postponed_exception = std::current_exception();
cond.broadcast();
} }
if (!success) {
PostponeError(std::move(error));
return false;
}
return true;
} }
void void
@ -182,7 +174,7 @@ NfsInputStream::OnNfsFileRead(const void *data, size_t data_size)
} }
void void
NfsInputStream::OnNfsFileError(Error &&error) NfsInputStream::OnNfsFileError(std::exception_ptr &&e)
{ {
const ScopeLock protect(mutex); const ScopeLock protect(mutex);
@ -197,7 +189,7 @@ NfsInputStream::OnNfsFileError(Error &&error)
return; return;
} }
postponed_error = std::move(error); postponed_exception = std::move(e);
if (IsSeekPending()) if (IsSeekPending())
SeekDone(); SeekDone();

View File

@ -20,12 +20,10 @@
#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 void
BlockingNfsOperation::Run(Error &_error) BlockingNfsOperation::Run()
{ {
/* subscribe to the connection, which will invoke either /* subscribe to the connection, which will invoke either
OnNfsConnectionReady() or OnNfsConnectionFailed() */ OnNfsConnectionReady() or OnNfsConnectionFailed() */
@ -33,40 +31,37 @@ BlockingNfsOperation::Run(Error &_error)
[this](){ connection.AddLease(*this); }); [this](){ connection.AddLease(*this); });
/* wait for completion */ /* wait for completion */
if (!LockWaitFinished()) { if (!LockWaitFinished())
_error.Set(nfs_domain, 0, "Timeout"); throw std::runtime_error("Timeout");
return false;
}
/* check for error */ /* check for error */
if (error.IsDefined()) { if (error)
_error = std::move(error); std::rethrow_exception(std::move(error));
return false;
}
return true;
} }
void void
BlockingNfsOperation::OnNfsConnectionReady() BlockingNfsOperation::OnNfsConnectionReady()
{ {
if (!Start(error)) { try {
Start();
} catch (...) {
error = std::current_exception();
connection.RemoveLease(*this); connection.RemoveLease(*this);
LockSetFinished(); LockSetFinished();
} }
} }
void void
BlockingNfsOperation::OnNfsConnectionFailed(const Error &_error) BlockingNfsOperation::OnNfsConnectionFailed(std::exception_ptr e)
{ {
error.Set(_error); error = std::move(e);
LockSetFinished(); LockSetFinished();
} }
void void
BlockingNfsOperation::OnNfsConnectionDisconnected(const Error &_error) BlockingNfsOperation::OnNfsConnectionDisconnected(std::exception_ptr e)
{ {
error.Set(_error); error = std::move(e);
LockSetFinished(); LockSetFinished();
} }
@ -80,10 +75,10 @@ BlockingNfsOperation::OnNfsCallback(unsigned status, void *data)
} }
void void
BlockingNfsOperation::OnNfsError(Error &&_error) BlockingNfsOperation::OnNfsError(std::exception_ptr &&e)
{ {
connection.RemoveLease(*this); connection.RemoveLease(*this);
error = std::move(_error); error = std::move(e);
LockSetFinished(); LockSetFinished();
} }

View File

@ -25,7 +25,8 @@
#include "Lease.hxx" #include "Lease.hxx"
#include "thread/Mutex.hxx" #include "thread/Mutex.hxx"
#include "thread/Cond.hxx" #include "thread/Cond.hxx"
#include "util/Error.hxx"
#include <exception>
class NfsConnection; class NfsConnection;
@ -42,7 +43,7 @@ class BlockingNfsOperation : protected NfsCallback, NfsLease {
bool finished; bool finished;
Error error; std::exception_ptr error;
protected: protected:
NfsConnection &connection; NfsConnection &connection;
@ -51,7 +52,10 @@ public:
BlockingNfsOperation(NfsConnection &_connection) BlockingNfsOperation(NfsConnection &_connection)
:finished(false), connection(_connection) {} :finished(false), connection(_connection) {}
bool Run(Error &error); /**
* Throws std::runtime_error on error.
*/
void Run();
private: private:
bool LockWaitFinished() { bool LockWaitFinished() {
@ -75,15 +79,15 @@ private:
/* virtual methods from NfsLease */ /* virtual methods from NfsLease */
void OnNfsConnectionReady() final; void OnNfsConnectionReady() final;
void OnNfsConnectionFailed(const Error &error) final; void OnNfsConnectionFailed(std::exception_ptr e) final;
void OnNfsConnectionDisconnected(const Error &error) final; void OnNfsConnectionDisconnected(std::exception_ptr e) final;
/* virtual methods from NfsCallback */ /* virtual methods from NfsCallback */
void OnNfsCallback(unsigned status, void *data) final; void OnNfsCallback(unsigned status, void *data) final;
void OnNfsError(Error &&error) final; void OnNfsError(std::exception_ptr &&e) final;
protected: protected:
virtual bool Start(Error &error) = 0; virtual void Start() = 0;
virtual void HandleResult(unsigned status, void *data) = 0; virtual void HandleResult(unsigned status, void *data) = 0;
}; };

View File

@ -22,12 +22,12 @@
#include "check.h" #include "check.h"
class Error; #include <exception>
class NfsCallback { class NfsCallback {
public: public:
virtual void OnNfsCallback(unsigned status, void *data) = 0; virtual void OnNfsCallback(unsigned status, void *data) = 0;
virtual void OnNfsError(Error &&error) = 0; virtual void OnNfsError(std::exception_ptr &&e) = 0;
}; };
#endif #endif

View File

@ -20,11 +20,10 @@
#include "config.h" #include "config.h"
#include "Connection.hxx" #include "Connection.hxx"
#include "Lease.hxx" #include "Lease.hxx"
#include "Domain.hxx"
#include "Callback.hxx" #include "Callback.hxx"
#include "event/Loop.hxx" #include "event/Loop.hxx"
#include "system/fd_util.h" #include "system/fd_util.h"
#include "util/Error.hxx" #include "util/RuntimeError.hxx"
extern "C" { extern "C" {
#include <nfsc/libnfs.h> #include <nfsc/libnfs.h>
@ -36,90 +35,65 @@ extern "C" {
static constexpr unsigned NFS_MOUNT_TIMEOUT = 60; static constexpr unsigned NFS_MOUNT_TIMEOUT = 60;
inline bool inline void
NfsConnection::CancellableCallback::Stat(nfs_context *ctx, NfsConnection::CancellableCallback::Stat(nfs_context *ctx,
const char *path, const char *path)
Error &error)
{ {
assert(connection.GetEventLoop().IsInside()); 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", throw FormatRuntimeError("nfs_stat_async() failed: %s",
nfs_get_error(ctx)); nfs_get_error(ctx));
return false;
}
return true;
} }
inline bool inline void
NfsConnection::CancellableCallback::OpenDirectory(nfs_context *ctx, NfsConnection::CancellableCallback::OpenDirectory(nfs_context *ctx,
const char *path, const char *path)
Error &error)
{ {
assert(connection.GetEventLoop().IsInside()); 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", throw FormatRuntimeError("nfs_opendir_async() failed: %s",
nfs_get_error(ctx)); nfs_get_error(ctx));
return false;
}
return true;
} }
inline bool inline void
NfsConnection::CancellableCallback::Open(nfs_context *ctx, NfsConnection::CancellableCallback::Open(nfs_context *ctx,
const char *path, int flags, const char *path, int flags)
Error &error)
{ {
assert(connection.GetEventLoop().IsInside()); 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)
error.Format(nfs_domain, "nfs_open_async() failed: %s", throw FormatRuntimeError("nfs_open_async() failed: %s",
nfs_get_error(ctx)); nfs_get_error(ctx));
return false;
}
return true;
} }
inline bool inline void
NfsConnection::CancellableCallback::Stat(nfs_context *ctx, NfsConnection::CancellableCallback::Stat(nfs_context *ctx,
struct nfsfh *fh, struct nfsfh *fh)
Error &error)
{ {
assert(connection.GetEventLoop().IsInside()); 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", throw FormatRuntimeError("nfs_fstat_async() failed: %s",
nfs_get_error(ctx)); nfs_get_error(ctx));
return false;
}
return true;
} }
inline bool inline void
NfsConnection::CancellableCallback::Read(nfs_context *ctx, struct nfsfh *fh, NfsConnection::CancellableCallback::Read(nfs_context *ctx, struct nfsfh *fh,
uint64_t offset, size_t size, uint64_t offset, size_t size)
Error &error)
{ {
assert(connection.GetEventLoop().IsInside()); 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", throw FormatRuntimeError("nfs_pread_async() failed: %s",
nfs_get_error(ctx)); nfs_get_error(ctx));
return false;
}
return true;
} }
inline void inline void
@ -160,8 +134,7 @@ NfsConnection::CancellableCallback::Callback(int err, void *data)
if (err >= 0) if (err >= 0)
cb.OnNfsCallback((unsigned)err, data); cb.OnNfsCallback((unsigned)err, data);
else else
cb.OnNfsError(Error(nfs_domain, err, cb.OnNfsError(std::make_exception_ptr(std::runtime_error((const char *)data)));
(const char *)data));
} else { } else {
if (open) { if (open) {
/* a nfs_open_async() call was cancelled - to /* a nfs_open_async() call was cancelled - to
@ -234,37 +207,38 @@ NfsConnection::RemoveLease(NfsLease &lease)
active_leases.remove(&lease); active_leases.remove(&lease);
} }
bool void
NfsConnection::Stat(const char *path, NfsCallback &callback, Error &error) NfsConnection::Stat(const char *path, NfsCallback &callback)
{ {
assert(GetEventLoop().IsInside()); 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);
if (!c.Stat(context, path, error)) { try {
c.Stat(context, path);
} catch (...) {
callbacks.Remove(c); callbacks.Remove(c);
return false; throw;
} }
ScheduleSocket(); ScheduleSocket();
return true;
} }
bool void
NfsConnection::OpenDirectory(const char *path, NfsCallback &callback, NfsConnection::OpenDirectory(const char *path, NfsCallback &callback)
Error &error)
{ {
assert(GetEventLoop().IsInside()); 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);
if (!c.OpenDirectory(context, path, error)) { try {
c.OpenDirectory(context, path);
} catch (...) {
callbacks.Remove(c); callbacks.Remove(c);
return false; throw;
} }
ScheduleSocket(); ScheduleSocket();
return true;
} }
const struct nfsdirent * const struct nfsdirent *
@ -283,54 +257,56 @@ NfsConnection::CloseDirectory(struct nfsdir *dir)
return nfs_closedir(context, dir); return nfs_closedir(context, dir);
} }
bool void
NfsConnection::Open(const char *path, int flags, NfsCallback &callback, NfsConnection::Open(const char *path, int flags, NfsCallback &callback)
Error &error)
{ {
assert(GetEventLoop().IsInside()); 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);
if (!c.Open(context, path, flags, error)) { try {
c.Open(context, path, flags);
} catch (...) {
callbacks.Remove(c); callbacks.Remove(c);
return false; throw;
} }
ScheduleSocket(); ScheduleSocket();
return true;
} }
bool void
NfsConnection::Stat(struct nfsfh *fh, NfsCallback &callback, Error &error) NfsConnection::Stat(struct nfsfh *fh, NfsCallback &callback)
{ {
assert(GetEventLoop().IsInside()); 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);
if (!c.Stat(context, fh, error)) { try {
c.Stat(context, fh);
} catch (...) {
callbacks.Remove(c); callbacks.Remove(c);
return false; throw;
} }
ScheduleSocket(); ScheduleSocket();
return true;
} }
bool void
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)
{ {
assert(GetEventLoop().IsInside()); 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);
if (!c.Read(context, fh, offset, size, error)) { try {
c.Read(context, fh, offset, size);
} catch (...) {
callbacks.Remove(c); callbacks.Remove(c);
return false; throw;
} }
ScheduleSocket(); ScheduleSocket();
return true;
} }
void void
@ -479,7 +455,7 @@ NfsConnection::OnSocketReady(unsigned flags)
} }
if (!was_mounted && mount_finished) { if (!was_mounted && mount_finished) {
if (postponed_mount_error.IsDefined()) { if (postponed_mount_error) {
DestroyContext(); DestroyContext();
closed = true; closed = true;
BroadcastMountError(std::move(postponed_mount_error)); BroadcastMountError(std::move(postponed_mount_error));
@ -487,11 +463,10 @@ NfsConnection::OnSocketReady(unsigned flags)
BroadcastMountSuccess(); BroadcastMountSuccess();
} else if (result < 0) { } else if (result < 0) {
/* the connection has failed */ /* the connection has failed */
Error error;
error.Format(nfs_domain, "NFS connection has failed: %s",
nfs_get_error(context));
BroadcastError(std::move(error)); auto e = FormatRuntimeError("NFS connection has failed: %s",
nfs_get_error(context));
BroadcastError(std::make_exception_ptr(e));
DestroyContext(); DestroyContext();
closed = true; closed = true;
@ -499,15 +474,13 @@ NfsConnection::OnSocketReady(unsigned flags)
/* this happens when rpc_reconnect_requeue() is called /* this happens when rpc_reconnect_requeue() is called
after the connection broke, but autoreconnect was after the connection broke, but autoreconnect was
disabled - nfs_service() returns 0 */ disabled - nfs_service() returns 0 */
Error error;
const char *msg = nfs_get_error(context); const char *msg = nfs_get_error(context);
if (msg == nullptr) if (msg == nullptr)
error.Set(nfs_domain, "NFS socket disappeared"); msg = "<unknown>";
else auto e = FormatRuntimeError("NFS socket disappeared: %s", msg);
error.Format(nfs_domain,
"NFS socket disappeared: %s", msg);
BroadcastError(std::move(error)); BroadcastError(std::make_exception_ptr(e));
DestroyContext(); DestroyContext();
closed = true; closed = true;
@ -539,9 +512,9 @@ NfsConnection::MountCallback(int status, gcc_unused nfs_context *nfs,
TimeoutMonitor::Cancel(); TimeoutMonitor::Cancel();
if (status < 0) { if (status < 0) {
postponed_mount_error.Format(nfs_domain, status, auto e = FormatRuntimeError("nfs_mount_async() failed: %s",
"nfs_mount_async() failed: %s", nfs_get_error(context));
nfs_get_error(context)); postponed_mount_error = std::make_exception_ptr(e);
return; return;
} }
} }
@ -555,19 +528,17 @@ NfsConnection::MountCallback(int status, nfs_context *nfs, void *data,
c->MountCallback(status, nfs, data); c->MountCallback(status, nfs, data);
} }
inline bool inline void
NfsConnection::MountInternal(Error &error) NfsConnection::MountInternal()
{ {
assert(GetEventLoop().IsInside()); assert(GetEventLoop().IsInside());
assert(context == nullptr); assert(context == nullptr);
context = nfs_init_context(); context = nfs_init_context();
if (context == nullptr) { if (context == nullptr)
error.Set(nfs_domain, "nfs_init_context() failed"); throw std::runtime_error("nfs_init_context() failed");
return false;
}
postponed_mount_error.Clear(); postponed_mount_error = std::exception_ptr();
mount_finished = false; mount_finished = false;
TimeoutMonitor::ScheduleSeconds(NFS_MOUNT_TIMEOUT); TimeoutMonitor::ScheduleSeconds(NFS_MOUNT_TIMEOUT);
@ -580,16 +551,14 @@ NfsConnection::MountInternal(Error &error)
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) {
error.Format(nfs_domain, auto e = FormatRuntimeError("nfs_mount_async() failed: %s",
"nfs_mount_async() failed: %s", nfs_get_error(context));
nfs_get_error(context));
nfs_destroy_context(context); nfs_destroy_context(context);
context = nullptr; context = nullptr;
return false; throw e;
} }
ScheduleSocket(); ScheduleSocket();
return true;
} }
void void
@ -605,31 +574,31 @@ NfsConnection::BroadcastMountSuccess()
} }
void void
NfsConnection::BroadcastMountError(Error &&error) NfsConnection::BroadcastMountError(std::exception_ptr &&e)
{ {
assert(GetEventLoop().IsInside()); assert(GetEventLoop().IsInside());
while (!new_leases.empty()) { while (!new_leases.empty()) {
auto l = new_leases.front(); auto l = new_leases.front();
new_leases.pop_front(); new_leases.pop_front();
l->OnNfsConnectionFailed(error); l->OnNfsConnectionFailed(e);
} }
OnNfsConnectionError(std::move(error)); OnNfsConnectionError(std::move(e));
} }
void void
NfsConnection::BroadcastError(Error &&error) NfsConnection::BroadcastError(std::exception_ptr &&e)
{ {
assert(GetEventLoop().IsInside()); assert(GetEventLoop().IsInside());
while (!active_leases.empty()) { while (!active_leases.empty()) {
auto l = active_leases.front(); auto l = active_leases.front();
active_leases.pop_front(); active_leases.pop_front();
l->OnNfsConnectionDisconnected(error); l->OnNfsConnectionDisconnected(e);
} }
BroadcastMountError(std::move(error)); BroadcastMountError(std::move(e));
} }
void void
@ -641,7 +610,7 @@ NfsConnection::OnTimeout()
mount_finished = true; mount_finished = true;
DestroyContext(); DestroyContext();
BroadcastMountError(Error(nfs_domain, "Mount timeout")); BroadcastMountError(std::make_exception_ptr(std::runtime_error("Mount timeout")));
} }
void void
@ -650,9 +619,10 @@ NfsConnection::RunDeferred()
assert(GetEventLoop().IsInside()); assert(GetEventLoop().IsInside());
if (context == nullptr) { if (context == nullptr) {
Error error; try {
if (!MountInternal(error)) { MountInternal();
BroadcastMountError(std::move(error)); } catch (...) {
BroadcastMountError(std::current_exception());
return; return;
} }
} }

View File

@ -24,11 +24,11 @@
#include "event/SocketMonitor.hxx" #include "event/SocketMonitor.hxx"
#include "event/TimeoutMonitor.hxx" #include "event/TimeoutMonitor.hxx"
#include "event/DeferredMonitor.hxx" #include "event/DeferredMonitor.hxx"
#include "util/Error.hxx"
#include <string> #include <string>
#include <list> #include <list>
#include <forward_list> #include <forward_list>
#include <exception>
struct nfs_context; struct nfs_context;
struct nfsdir; struct nfsdir;
@ -65,17 +65,12 @@ class NfsConnection : SocketMonitor, TimeoutMonitor, DeferredMonitor {
connection(_connection), connection(_connection),
open(_open), close_fh(nullptr) {} open(_open), close_fh(nullptr) {}
bool Stat(nfs_context *context, const char *path, void Stat(nfs_context *context, const char *path);
Error &error); void OpenDirectory(nfs_context *context, const char *path);
bool OpenDirectory(nfs_context *context, const char *path, void Open(nfs_context *context, const char *path, int flags);
Error &error); void Stat(nfs_context *context, struct nfsfh *fh);
bool Open(nfs_context *context, const char *path, int flags, void Read(nfs_context *context, struct nfsfh *fh,
Error &error); uint64_t offset, size_t size);
bool Stat(nfs_context *context, struct nfsfh *fh,
Error &error);
bool Read(nfs_context *context, struct nfsfh *fh,
uint64_t offset, size_t size,
Error &error);
/** /**
* Cancel the operation and schedule a call to * Cancel the operation and schedule a call to
@ -115,7 +110,7 @@ class NfsConnection : SocketMonitor, TimeoutMonitor, DeferredMonitor {
*/ */
std::forward_list<struct nfsfh *> deferred_close; std::forward_list<struct nfsfh *> deferred_close;
Error postponed_mount_error; std::exception_ptr postponed_mount_error;
#ifndef NDEBUG #ifndef NDEBUG
/** /**
@ -175,25 +170,32 @@ public:
void AddLease(NfsLease &lease); void AddLease(NfsLease &lease);
void RemoveLease(NfsLease &lease); void RemoveLease(NfsLease &lease);
bool Stat(const char *path, NfsCallback &callback, Error &error); void Stat(const char *path, NfsCallback &callback);
bool OpenDirectory(const char *path, NfsCallback &callback, void OpenDirectory(const char *path, NfsCallback &callback);
Error &error);
const struct nfsdirent *ReadDirectory(struct nfsdir *dir); const struct nfsdirent *ReadDirectory(struct nfsdir *dir);
void CloseDirectory(struct nfsdir *dir); void CloseDirectory(struct nfsdir *dir);
bool Open(const char *path, int flags, NfsCallback &callback, /**
Error &error); * Throws std::runtime_error on error.
bool Stat(struct nfsfh *fh, NfsCallback &callback, Error &error); */
bool Read(struct nfsfh *fh, uint64_t offset, size_t size, void Open(const char *path, int flags, NfsCallback &callback);
NfsCallback &callback, Error &error);
void Stat(struct nfsfh *fh, NfsCallback &callback);
/**
* Throws std::runtime_error on error.
*/
void Read(struct nfsfh *fh, uint64_t offset, size_t size,
NfsCallback &callback);
void Cancel(NfsCallback &callback); void Cancel(NfsCallback &callback);
void Close(struct nfsfh *fh); void Close(struct nfsfh *fh);
void CancelAndClose(struct nfsfh *fh, NfsCallback &callback); void CancelAndClose(struct nfsfh *fh, NfsCallback &callback);
protected: protected:
virtual void OnNfsConnectionError(Error &&error) = 0; virtual void OnNfsConnectionError(std::exception_ptr &&e) = 0;
private: private:
void DestroyContext(); void DestroyContext();
@ -208,10 +210,10 @@ private:
*/ */
void DeferClose(struct nfsfh *fh); void DeferClose(struct nfsfh *fh);
bool MountInternal(Error &error); void MountInternal();
void BroadcastMountSuccess(); void BroadcastMountSuccess();
void BroadcastMountError(Error &&error); void BroadcastMountError(std::exception_ptr &&e);
void BroadcastError(Error &&error); void BroadcastError(std::exception_ptr &&e);
static void MountCallback(int status, nfs_context *nfs, void *data, static void MountCallback(int status, nfs_context *nfs, void *data,
void *private_data); void *private_data);

View File

@ -1,24 +0,0 @@
/*
* Copyright 2003-2016 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "Domain.hxx"
#include "util/Domain.hxx"
const Domain nfs_domain("nfs");

View File

@ -1,27 +0,0 @@
/*
* Copyright 2003-2016 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_NFS_DOMAIN_HXX
#define MPD_NFS_DOMAIN_HXX
class Domain;
extern const Domain nfs_domain;
#endif

View File

@ -22,11 +22,9 @@
#include "Glue.hxx" #include "Glue.hxx"
#include "Base.hxx" #include "Base.hxx"
#include "Connection.hxx" #include "Connection.hxx"
#include "Domain.hxx"
#include "event/Call.hxx" #include "event/Call.hxx"
#include "IOThread.hxx" #include "IOThread.hxx"
#include "util/StringCompare.hxx" #include "util/StringCompare.hxx"
#include "util/Error.hxx"
#include <utility> #include <utility>
@ -128,16 +126,13 @@ NfsFileReader::Open(const char *uri)
DeferredMonitor::Schedule(); DeferredMonitor::Schedule();
} }
bool void
NfsFileReader::Read(uint64_t offset, size_t size, Error &error) NfsFileReader::Read(uint64_t offset, size_t size)
{ {
assert(state == State::IDLE); assert(state == State::IDLE);
if (!connection->Read(fh, offset, size, *this, error)) connection->Read(fh, offset, size, *this);
return false;
state = State::READ; state = State::READ;
return true;
} }
void void
@ -154,9 +149,10 @@ NfsFileReader::OnNfsConnectionReady()
{ {
assert(state == State::MOUNT); assert(state == State::MOUNT);
Error error; try {
if (!connection->Open(path, O_RDONLY, *this, error)) { connection->Open(path, O_RDONLY, *this);
OnNfsFileError(std::move(error)); } catch (...) {
OnNfsFileError(std::current_exception());
return; return;
} }
@ -164,27 +160,23 @@ NfsFileReader::OnNfsConnectionReady()
} }
void void
NfsFileReader::OnNfsConnectionFailed(const Error &error) NfsFileReader::OnNfsConnectionFailed(std::exception_ptr e)
{ {
assert(state == State::MOUNT); assert(state == State::MOUNT);
state = State::INITIAL; state = State::INITIAL;
Error copy; OnNfsFileError(std::move(e));
copy.Set(error);
OnNfsFileError(std::move(copy));
} }
void void
NfsFileReader::OnNfsConnectionDisconnected(const Error &error) NfsFileReader::OnNfsConnectionDisconnected(std::exception_ptr e)
{ {
assert(state > State::MOUNT); assert(state > State::MOUNT);
CancelOrClose(); CancelOrClose();
Error copy; OnNfsFileError(std::move(e));
copy.Set(error);
OnNfsFileError(std::move(copy));
} }
inline void inline void
@ -196,9 +188,10 @@ NfsFileReader::OpenCallback(nfsfh *_fh)
fh = _fh; fh = _fh;
Error error; try {
if (!connection->Stat(fh, *this, error)) { connection->Stat(fh, *this);
OnNfsFileError(std::move(error)); } catch (...) {
OnNfsFileError(std::current_exception());
return; return;
} }
@ -214,7 +207,7 @@ NfsFileReader::StatCallback(const struct stat *st)
assert(st != nullptr); assert(st != nullptr);
if (!S_ISREG(st->st_mode)) { if (!S_ISREG(st->st_mode)) {
OnNfsFileError(Error(nfs_domain, "Not a regular file")); OnNfsFileError(std::make_exception_ptr(std::runtime_error("Not a regular file")));
return; return;
} }
@ -250,7 +243,7 @@ NfsFileReader::OnNfsCallback(unsigned status, void *data)
} }
void void
NfsFileReader::OnNfsError(Error &&error) NfsFileReader::OnNfsError(std::exception_ptr &&e)
{ {
switch (state) { switch (state) {
case State::INITIAL: case State::INITIAL:
@ -276,7 +269,7 @@ NfsFileReader::OnNfsError(Error &&error)
break; break;
} }
OnNfsFileError(std::move(error)); OnNfsFileError(std::move(e));
} }
void void

View File

@ -27,6 +27,7 @@
#include "Compiler.h" #include "Compiler.h"
#include <string> #include <string>
#include <exception>
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
@ -66,7 +67,10 @@ public:
*/ */
void Open(const char *uri); void Open(const char *uri);
bool Read(uint64_t offset, size_t size, Error &error); /**
* Throws std::runtime_error on error.
*/
void Read(uint64_t offset, size_t size);
void CancelRead(); void CancelRead();
bool IsIdle() const { bool IsIdle() const {
@ -76,7 +80,7 @@ public:
protected: protected:
virtual void OnNfsFileOpen(uint64_t size) = 0; virtual void OnNfsFileOpen(uint64_t size) = 0;
virtual void OnNfsFileRead(const void *data, size_t size) = 0; virtual void OnNfsFileRead(const void *data, size_t size) = 0;
virtual void OnNfsFileError(Error &&error) = 0; virtual void OnNfsFileError(std::exception_ptr &&e) = 0;
private: private:
/** /**
@ -90,12 +94,12 @@ private:
/* virtual methods from NfsLease */ /* virtual methods from NfsLease */
void OnNfsConnectionReady() final; void OnNfsConnectionReady() final;
void OnNfsConnectionFailed(const Error &error) final; void OnNfsConnectionFailed(std::exception_ptr e) final;
void OnNfsConnectionDisconnected(const Error &error) final; void OnNfsConnectionDisconnected(std::exception_ptr e) final;
/* virtual methods from NfsCallback */ /* virtual methods from NfsCallback */
void OnNfsCallback(unsigned status, void *data) final; void OnNfsCallback(unsigned status, void *data) final;
void OnNfsError(Error &&error) final; void OnNfsError(std::exception_ptr &&e) final;
/* virtual methods from DeferredMonitor */ /* virtual methods from DeferredMonitor */
void RunDeferred() final; void RunDeferred() final;

View File

@ -22,6 +22,8 @@
#include "check.h" #include "check.h"
#include <exception>
class Error; class Error;
class NfsLease { class NfsLease {
@ -36,13 +38,13 @@ public:
* The #NfsConnection has failed to mount the server's export. * The #NfsConnection has failed to mount the server's export.
* This is being called instead of OnNfsConnectionReady(). * This is being called instead of OnNfsConnectionReady().
*/ */
virtual void OnNfsConnectionFailed(const Error &error) = 0; virtual void OnNfsConnectionFailed(std::exception_ptr e) = 0;
/** /**
* The #NfsConnection has failed after OnNfsConnectionReady() * The #NfsConnection has failed after OnNfsConnectionReady()
* had been called already. * had been called already.
*/ */
virtual void OnNfsConnectionDisconnected(const Error &error) = 0; virtual void OnNfsConnectionDisconnected(std::exception_ptr e) = 0;
}; };
#endif #endif

View File

@ -26,9 +26,9 @@
#include <string.h> #include <string.h>
void void
NfsManager::ManagedConnection::OnNfsConnectionError(Error &&error) NfsManager::ManagedConnection::OnNfsConnectionError(std::exception_ptr &&e)
{ {
FormatError(error, "NFS error on %s:%s", GetServer(), GetExportName()); FormatError(e, "NFS error on %s:%s", GetServer(), GetExportName());
/* defer deletion so the caller /* defer deletion so the caller
(i.e. NfsConnection::OnSocketReady()) can still use this (i.e. NfsConnection::OnSocketReady()) can still use this

View File

@ -53,7 +53,7 @@ class NfsManager final : IdleMonitor {
protected: protected:
/* virtual methods from NfsConnection */ /* virtual methods from NfsConnection */
void OnNfsConnectionError(Error &&error) override; void OnNfsConnectionError(std::exception_ptr &&e) override;
}; };
struct Compare { struct Compare {

View File

@ -24,7 +24,6 @@
#include "storage/FileInfo.hxx" #include "storage/FileInfo.hxx"
#include "storage/MemoryDirectoryReader.hxx" #include "storage/MemoryDirectoryReader.hxx"
#include "lib/nfs/Blocking.hxx" #include "lib/nfs/Blocking.hxx"
#include "lib/nfs/Domain.hxx"
#include "lib/nfs/Base.hxx" #include "lib/nfs/Base.hxx"
#include "lib/nfs/Lease.hxx" #include "lib/nfs/Lease.hxx"
#include "lib/nfs/Connection.hxx" #include "lib/nfs/Connection.hxx"
@ -66,7 +65,7 @@ class NfsStorage final
Mutex mutex; Mutex mutex;
Cond cond; Cond cond;
State state; State state;
Error last_error; std::exception_ptr last_exception;
public: public:
NfsStorage(EventLoop &_loop, const char *_base, NfsStorage(EventLoop &_loop, const char *_base,
@ -102,17 +101,17 @@ public:
SetState(State::READY); SetState(State::READY);
} }
void OnNfsConnectionFailed(gcc_unused const Error &error) final { void OnNfsConnectionFailed(std::exception_ptr e) final {
assert(state == State::CONNECTING); assert(state == State::CONNECTING);
SetState(State::DELAY, error); SetState(State::DELAY, std::move(e));
TimeoutMonitor::ScheduleSeconds(60); TimeoutMonitor::ScheduleSeconds(60);
} }
void OnNfsConnectionDisconnected(gcc_unused const Error &error) final { void OnNfsConnectionDisconnected(std::exception_ptr e) final {
assert(state == State::READY); assert(state == State::READY);
SetState(State::DELAY, error); SetState(State::DELAY, std::move(e));
TimeoutMonitor::ScheduleSeconds(5); TimeoutMonitor::ScheduleSeconds(5);
} }
@ -142,13 +141,12 @@ private:
cond.broadcast(); cond.broadcast();
} }
void SetState(State _state, const Error &error) { void SetState(State _state, std::exception_ptr &&e) {
assert(GetEventLoop().IsInside()); assert(GetEventLoop().IsInside());
const ScopeLock protect(mutex); const ScopeLock protect(mutex);
state = _state; state = _state;
last_error.Clear(); last_exception = std::move(e);
last_error.Set(error);
cond.broadcast(); cond.broadcast();
} }
@ -168,7 +166,7 @@ private:
Connect(); Connect();
} }
bool WaitConnected(Error &error) { void WaitConnected() {
const ScopeLock protect(mutex); const ScopeLock protect(mutex);
while (true) { while (true) {
@ -184,12 +182,11 @@ private:
case State::CONNECTING: case State::CONNECTING:
case State::READY: case State::READY:
return true; return;
case State::DELAY: case State::DELAY:
assert(last_error.IsDefined()); assert(last_exception);
error.Set(last_error); std::rethrow_exception(last_exception);
return false;
} }
} }
} }
@ -271,8 +268,8 @@ public:
:BlockingNfsOperation(_connection), path(_path), info(_info) {} :BlockingNfsOperation(_connection), path(_path), info(_info) {}
protected: protected:
bool Start(Error &_error) override { void Start() override {
return connection.Stat(path, *this, _error); connection.Stat(path, *this);
} }
void HandleResult(gcc_unused unsigned status, void *data) override { void HandleResult(gcc_unused unsigned status, void *data) override {
@ -288,11 +285,11 @@ NfsStorage::GetInfo(const char *uri_utf8, gcc_unused bool follow,
if (path.empty()) if (path.empty())
return false; return false;
if (!WaitConnected(error)) WaitConnected();
return false;
NfsGetInfoOperation operation(*connection, path.c_str(), info); NfsGetInfoOperation operation(*connection, path.c_str(), info);
return operation.Run(error); operation.Run();
return true;
} }
gcc_pure gcc_pure
@ -342,8 +339,8 @@ public:
} }
protected: protected:
bool Start(Error &_error) override { void Start() override {
return connection.OpenDirectory(path, *this, _error); connection.OpenDirectory(path, *this);
} }
void HandleResult(gcc_unused unsigned status, void *data) override { void HandleResult(gcc_unused unsigned status, void *data) override {
@ -386,19 +383,16 @@ NfsStorage::OpenDirectory(const char *uri_utf8, Error &error)
if (path.empty()) if (path.empty())
return nullptr; return nullptr;
if (!WaitConnected(error)) WaitConnected();
return nullptr;
NfsListDirectoryOperation operation(*connection, path.c_str()); NfsListDirectoryOperation operation(*connection, path.c_str());
if (!operation.Run(error)) operation.Run();
return nullptr;
return operation.ToReader(); return operation.ToReader();
} }
static Storage * static Storage *
CreateNfsStorageURI(EventLoop &event_loop, const char *base, CreateNfsStorageURI(EventLoop &event_loop, const char *base, Error &)
Error &error)
{ {
if (memcmp(base, "nfs://", 6) != 0) if (memcmp(base, "nfs://", 6) != 0)
return nullptr; return nullptr;
@ -406,10 +400,8 @@ CreateNfsStorageURI(EventLoop &event_loop, const char *base,
const char *p = base + 6; const char *p = base + 6;
const char *mount = strchr(p, '/'); const char *mount = strchr(p, '/');
if (mount == nullptr) { if (mount == nullptr)
error.Set(nfs_domain, "Malformed nfs:// URI"); throw std::runtime_error("Malformed nfs:// URI");
return nullptr;
}
const std::string server(p, mount); const std::string server(p, mount);