summaryrefslogtreecommitdiff
path: root/src/mongo/util/interruptible.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/util/interruptible.h')
-rw-r--r--src/mongo/util/interruptible.h245
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;