diff --git a/ircd/fs_aio.cc b/ircd/fs_aio.cc index 70b2a70eb..3f0c627e6 100644 --- a/ircd/fs_aio.cc +++ b/ircd/fs_aio.cc @@ -422,7 +422,6 @@ ircd::fs::aio::request::operator()() { assert(system); assert(ctx::current); - assert(waiter == ctx::current); const size_t submitted_bytes { @@ -446,7 +445,7 @@ ircd::fs::aio::request::operator()() system->submit(*this); // Wait for completion - while(!system->wait(*this)); + while(!wait()); assert(completed()); assert(retval <= ssize_t(submitted_bytes)); @@ -491,6 +490,47 @@ ircd::fs::aio::request::operator()() }; } +/// Block the current context while waiting for results. +/// +/// This function returns true when the request completes and it's safe to +/// continue. This function intercepts all exceptions and cancels the request +/// if it's appropriate before rethrowing; after which it is safe to continue. +/// +/// If this function returns false it is not safe to continue; it *must* be +/// called again until it no longer returns false. +bool +ircd::fs::aio::request::wait() +try +{ + waiter.wait([this] + { + return completed(); + }); + + return true; +} +catch(...) +{ + // When the ctx is interrupted we're obliged to cancel the request + // if it has not reached a completed state. + if(completed()) + throw; + + // The handler callstack is invoked synchronously on this stack for + // requests which are still in our userspace queue. + if(queued()) + { + cancel(); + throw; + } + + // The handler callstack is invoked asynchronously for requests + // submitted to the kernel; we *must* wait for that by blocking + // ctx interrupts and terminations and continue to wait. The caller + // must loop into this call again until it returns true or throws. + return false; +} + bool ircd::fs::aio::request::queued() const @@ -669,46 +709,6 @@ ircd::fs::aio::system::wait() return true; } -/// Block the current context while waiting for results. -/// -/// This function returns true when the request completes and it's safe to -/// continue. This function intercepts all exceptions and cancels the request -/// if it's appropriate before rethrowing; after which it is safe to continue. -/// -/// If this function returns false it is not safe to continue; it *must* be -/// called again until it no longer returns false. -bool -ircd::fs::aio::system::wait(request &request) -try -{ - assert(ctx::current == request.waiter); - while(!request.completed()) - ctx::wait(); - - return true; -} -catch(...) -{ - // When the ctx is interrupted we're obliged to cancel the request - // if it has not reached a completed state. - if(request.completed()) - throw; - - // The handler callstack is invoked synchronously on this stack for - // requests which are still in our userspace queue. - if(request.queued()) - { - request.cancel(); - throw; - } - - // The handler callstack is invoked asynchronously for requests - // submitted to the kernel; we *must* wait for that by blocking - // ctx interrupts and terminations and continue to wait. The caller - // must loop into this call again until it returns true or throws. - return false; -} - bool ircd::fs::aio::system::cancel(request &request) try @@ -1181,8 +1181,7 @@ noexcept try // Notify the waiting context. Note that we are on the main async stack // but it is safe to notify from here. - assert(request->waiter); - ctx::notify(*request->waiter); + request->waiter.notify_one(); stats.events++; } catch(const std::exception &e) diff --git a/ircd/fs_aio.h b/ircd/fs_aio.h index 467060772..ce77a1b3c 100644 --- a/ircd/fs_aio.h +++ b/ircd/fs_aio.h @@ -125,12 +125,13 @@ struct ircd::fs::aio::request ssize_t retval {-2L}; ssize_t errcode {0L}; const struct opts *opts {nullptr}; - ctx::ctx *waiter {ctx::current}; + ctx::dock waiter; public: const_iovec_view iovec() const; bool completed() const; bool queued() const; + bool wait(); size_t operator()(); bool cancel();