diff options
Diffstat (limited to 'src/mongo/util/interruptible.h')
-rw-r--r-- | src/mongo/util/interruptible.h | 245 |
1 files changed, 92 insertions, 153 deletions
diff --git a/src/mongo/util/interruptible.h b/src/mongo/util/interruptible.h index 6e182d6bbd7..d72be7b77d0 100644 --- a/src/mongo/util/interruptible.h +++ b/src/mongo/util/interruptible.h @@ -41,21 +41,90 @@ namespace mongo { * A type which can be used to wait on condition variables with a level triggered one-way interrupt. * I.e. after the interrupt is triggered (via some non-public api call) subsequent calls to * waitForConditionXXX will fail. Interrupts must unblock all callers of waitForConditionXXX. + * + * This class should never be derived from directly. Instead, please derive from Interruptible. */ -class Interruptible { +class InterruptibleBase { +public: + virtual ~InterruptibleBase() = default; + + /** + * Returns the deadline for this InterruptibleBase, or Date_t::max() if there is no deadline. + */ + virtual Date_t getDeadline() const = 0; + + /** + * Returns Status::OK() unless this operation is in a killed state. + */ + virtual Status checkForInterruptNoAssert() noexcept = 0; + protected: + /** + * Same as Interruptible::waitForConditionOrInterruptUntil(), except returns + * StatusWith<stdx::cv_status> and a non-ok status indicates the error instead of a DBException. + */ + virtual StatusWith<stdx::cv_status> waitForConditionOrInterruptNoAssertUntil( + stdx::condition_variable& cv, BasicLockableAdapter m, Date_t deadline) noexcept = 0; + struct DeadlineState { Date_t deadline; ErrorCodes::Error error; bool hasArtificialDeadline; }; + /** + * Pushes a subsidiary deadline into the InterruptibleBase. Until an associated + * popArtificialDeadline() is invoked, the InterruptibleBase will fail checkForInterrupt() and + * waitForConditionOrInterrupt() calls with the passed error code if the deadline has passed. + * + * Note that deadline's higher than the current value are constrained (such that the passed + * error code will be returned/thrown, but after the min(oldDeadline, newDeadline) has passed). + * + * Returns state needed to pop the deadline. + */ + virtual DeadlineState pushArtificialDeadline(Date_t deadline, ErrorCodes::Error error) = 0; + + /** + * Pops the subsidiary deadline introduced by push. + */ + virtual void popArtificialDeadline(DeadlineState) = 0; + + /** + * Returns the equivalent of Date_t::now() + waitFor for the InterruptibleBase's clock + */ + virtual Date_t getExpirationDateForWaitForValue(Milliseconds waitFor) = 0; + struct IgnoreInterruptsState { bool ignoreInterrupts; DeadlineState deadline; }; /** + * Pushes an ignore interruption critical section into the InterruptibleBase. + * Until an associated popIgnoreInterrupts() is invoked, the InterruptibleBase should ignore + * interruptions related to explicit interruption or previously set deadlines. + * + * Note that new deadlines can be set after this is called, which will again introduce the + * possibility of interruption. + * + * Returns state needed to pop interruption. + */ + virtual IgnoreInterruptsState pushIgnoreInterrupts() = 0; + + /** + * Pops the ignored interruption critical section introduced by push. + */ + virtual void popIgnoreInterrupts(IgnoreInterruptsState iis) = 0; +}; + +/** + * An derived class of InterruptibleBase which provides a variety of helper functions + * + * Please derive from this class instead of InterruptibleBase. + */ +class Interruptible : public InterruptibleBase { +private: + /** * A deadline guard provides a subsidiary deadline to the parent. */ class DeadlineGuard { @@ -134,7 +203,7 @@ protected: public: /** * Returns a statically allocated instance that cannot be interrupted. Useful as a default - * argument to interruptible taking methods. + * argument to Interruptible-taking methods. */ static Interruptible* notInterruptible(); @@ -163,11 +232,6 @@ public: } /** - * Returns the deadline for this interruptible, or Date_t::max() if there is no deadline. - */ - virtual Date_t getDeadline() const = 0; - - /** * Invokes the passed callback with an interruption guard active. Additionally handles the * dance of try/catching the invocation and checking checkForInterrupt with the guard inactive * (to allow a higher level timeout to override a lower level one, or for top level interruption @@ -193,86 +257,9 @@ public: } /** - * Returns Status::OK() unless this operation is in a killed state. - */ - virtual Status checkForInterruptNoAssert() noexcept = 0; - - /** - * Waits for either the condition "cv" to be signaled, this operation to be interrupted, or the - * deadline on this operation to expire. In the event of interruption or operation deadline - * expiration, raises a AssertionException with an error code indicating the interruption type. - */ - template <typename LockT> - void waitForConditionOrInterrupt(stdx::condition_variable& cv, LockT& m) { - uassertStatusOK(waitForConditionOrInterruptNoAssert(cv, m)); - } - - /** - * Waits on condition "cv" for "pred" until "pred" returns true, or this operation - * is interrupted or its deadline expires. Throws a DBException for interruption and - * deadline expiration. - */ - template <typename LockT, typename PredicateT> - void waitForConditionOrInterrupt(stdx::condition_variable& cv, LockT& m, PredicateT pred) { - while (!pred()) { - waitForConditionOrInterrupt(cv, m); - } - } - - /** - * Same as waitForConditionOrInterrupt, except returns a Status instead of throwing - * a DBException to report interruption. - */ - template <typename LockT> - Status waitForConditionOrInterruptNoAssert(stdx::condition_variable& cv, LockT& m) noexcept { - auto status = waitForConditionOrInterruptNoAssertUntil(cv, m, Date_t::max()); - if (!status.isOK()) { - return status.getStatus(); - } - - invariant(status.getValue() == stdx::cv_status::no_timeout); - return Status::OK(); - } - - /** - * Same as the predicate form of waitForConditionOrInterrupt, except that it returns a not okay - * status instead of throwing on interruption. - */ - template <typename LockT, typename PredicateT> - Status waitForConditionOrInterruptNoAssert(stdx::condition_variable& cv, - LockT& m, - PredicateT pred) noexcept { - while (!pred()) { - auto status = waitForConditionOrInterruptNoAssert(cv, m); - - if (!status.isOK()) { - return status; - } - } - - return Status::OK(); - } - - /** - * Waits for condition "cv" to be signaled, or for the given "deadline" to expire, or - * for the operation to be interrupted, or for the operation's own deadline to expire. - * - * If the operation deadline expires or the operation is interrupted, throws a DBException. If - * the given "deadline" expires, returns cv_status::timeout. Otherwise, returns - * cv_status::no_timeout. - */ - template <typename LockT> - stdx::cv_status waitForConditionOrInterruptUntil(stdx::condition_variable& cv, - LockT& m, - Date_t deadline) { - return uassertStatusOK(waitForConditionOrInterruptNoAssertUntil(cv, m, deadline)); - } - - /** * Waits on condition "cv" for "pred" until "pred" returns true, or the given "deadline" * expires, or this operation is interrupted, or this operation's own deadline expires. * - * * If the operation deadline expires or the operation is interrupted, throws a DBException. If * the given "deadline" expires, returns cv_status::timeout. Otherwise, returns * cv_status::no_timeout indicating that "pred" finally returned true. @@ -282,24 +269,28 @@ public: LockT& m, Date_t deadline, PredicateT pred) { + auto waitUntil = [&](Date_t deadline) { + // Wrapping this in a lambda because it's a mouthful + return uassertStatusOK(waitForConditionOrInterruptNoAssertUntil(cv, m, deadline)); + }; + while (!pred()) { - if (stdx::cv_status::timeout == waitForConditionOrInterruptUntil(cv, m, deadline)) { + if (waitUntil(deadline) == stdx::cv_status::timeout) { return pred(); } - } + }; + return true; } /** - * Same as the non-predicate form of waitForConditionOrInterruptUntil, but takes a relative - * amount of time to wait instead of an absolute time point. + * Waits on condition "cv" for "pred" until "pred" returns true, or this operation + * is interrupted or its deadline expires. Throws a DBException for interruption and + * deadline expiration. */ - template <typename LockT> - stdx::cv_status waitForConditionOrInterruptFor(stdx::condition_variable& cv, - LockT& m, - Milliseconds ms) { - return uassertStatusOK( - waitForConditionOrInterruptNoAssertUntil(cv, m, getExpirationDateForWaitForValue(ms))); + template <typename LockT, typename PredicateT> + void waitForConditionOrInterrupt(stdx::condition_variable& cv, LockT& m, PredicateT pred) { + waitForConditionOrInterruptUntil(cv, m, Date_t::max(), std::move(pred)); } /** @@ -311,24 +302,12 @@ public: LockT& m, Milliseconds ms, PredicateT pred) { - const auto deadline = getExpirationDateForWaitForValue(ms); - while (!pred()) { - if (stdx::cv_status::timeout == waitForConditionOrInterruptUntil(cv, m, deadline)) { - return pred(); - } - } - return true; + return waitForConditionOrInterruptUntil( + cv, m, getExpirationDateForWaitForValue(ms), std::move(pred)); } /** - * Same as waitForConditionOrInterruptUntil, except returns StatusWith<stdx::cv_status> and - * non-ok status indicates the error instead of a DBException. - */ - virtual StatusWith<stdx::cv_status> waitForConditionOrInterruptNoAssertUntil( - stdx::condition_variable& cv, BasicLockableAdapter m, Date_t deadline) noexcept = 0; - - /** - * Sleeps until "deadline"; throws an exception if the interruptible is interrupted before then. + * Sleeps until "deadline"; throws an exception if the Interruptible is interrupted before then. */ void sleepUntil(Date_t deadline) { auto m = MONGO_MAKE_LATCH(); @@ -338,7 +317,7 @@ public: } /** - * Sleeps for "duration" ms; throws an exception if the interruptible is interrupted before + * Sleeps for "duration" ms; throws an exception if the Interruptible is interrupted before * then. */ void sleepFor(Milliseconds duration) { @@ -349,51 +328,11 @@ public: } protected: - /** - * Pushes an ignore interruption critical section into the interruptible. Until an associated - * popIgnoreInterrupts is invoked, the interruptible should ignore interruptions related to - * explicit interruption or previously set deadlines. - * - * Note that new deadlines can be set after this is called, which will again introduce the - * possibility of interruption. - * - * Returns state needed to pop interruption. - */ - virtual IgnoreInterruptsState pushIgnoreInterrupts() = 0; - - /** - * Pops the ignored interruption critical section introduced by push. - */ - virtual void popIgnoreInterrupts(IgnoreInterruptsState iis) = 0; - - /** - * Pushes a subsidiary deadline into the interruptible. Until an associated - * popArtificialDeadline is - * invoked, the interruptible will fail checkForInterrupt and waitForConditionOrInterrupt calls - * with the passed error code if the deadline has passed. - * - * Note that deadline's higher than the current value are constrained (such that the passed - * error code will be returned/thrown, but after the min(oldDeadline, newDeadline) has passed). - * - * Returns state needed to pop the deadline. - */ - virtual DeadlineState pushArtificialDeadline(Date_t deadline, ErrorCodes::Error error) = 0; - - /** - * Pops the subsidiary deadline introduced by push. - */ - virtual void popArtificialDeadline(DeadlineState) = 0; - - /** - * Returns the equivalent of Date_t::now() + waitFor for the interruptible's clock - */ - virtual Date_t getExpirationDateForWaitForValue(Milliseconds waitFor) = 0; - class NotInterruptible; }; /** - * A not interruptible type which can be used as a lightweight default arg for interruptible taking + * A non-interruptible type which can be used as a lightweight default arg for Interruptible-taking * functions. */ class Interruptible::NotInterruptible final : public Interruptible { @@ -417,9 +356,9 @@ class Interruptible::NotInterruptible final : public Interruptible { } // It's invalid to call the deadline or ignore interruption guards on a possibly noop - // interruptible. + // Interruptible. // - // The noop interruptible should only be invoked as a default arg at the bottom of the call + // The noop Interruptible should only be invoked as a default arg at the bottom of the call // stack (with types that won't modify it's invocation) IgnoreInterruptsState pushIgnoreInterrupts() override { MONGO_UNREACHABLE; |