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.h101
1 files changed, 92 insertions, 9 deletions
diff --git a/src/mongo/util/interruptible.h b/src/mongo/util/interruptible.h
index 1e9cc070c64..f7a02e971f9 100644
--- a/src/mongo/util/interruptible.h
+++ b/src/mongo/util/interruptible.h
@@ -107,6 +107,28 @@ protected:
* 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;
};
/**
@@ -172,6 +194,44 @@ private:
return DeadlineGuard(*this, deadline, error);
}
+ /**
+ * An interruption guard provides a region where interruption is ignored.
+ *
+ * Note that this causes the deadline to be reset to Date_t::max(), but that it can also be
+ * subsequently reduced in size after the fact.
+ */
+ class IgnoreInterruptionsGuard {
+ public:
+ IgnoreInterruptionsGuard(const IgnoreInterruptionsGuard&) = delete;
+ IgnoreInterruptionsGuard& operator=(const IgnoreInterruptionsGuard&) = delete;
+
+ IgnoreInterruptionsGuard(IgnoreInterruptionsGuard&& other)
+ : _interruptible(other._interruptible), _oldState(other._oldState) {
+ other._interruptible = nullptr;
+ }
+
+ IgnoreInterruptionsGuard& operator=(IgnoreInterruptionsGuard&&) = delete;
+
+ ~IgnoreInterruptionsGuard() {
+ if (_interruptible) {
+ _interruptible->popIgnoreInterrupts(_oldState);
+ }
+ }
+
+ private:
+ friend Interruptible;
+
+ explicit IgnoreInterruptionsGuard(Interruptible& interruptible)
+ : _interruptible(&interruptible), _oldState(_interruptible->pushIgnoreInterrupts()) {}
+
+ Interruptible* _interruptible;
+ IgnoreInterruptsState _oldState;
+ };
+
+ IgnoreInterruptionsGuard makeIgnoreInterruptionsGuard() {
+ return IgnoreInterruptionsGuard(*this);
+ }
+
public:
class WaitListener;
@@ -246,6 +306,24 @@ public:
}
/**
+ * 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
+ * to propagate)
+ */
+ template <typename Callback>
+ decltype(auto) runWithoutInterruptionExceptAtGlobalShutdown(Callback&& cb) {
+ try {
+ const auto guard = makeIgnoreInterruptionsGuard();
+ return std::forward<Callback>(cb)();
+ } catch (const ExceptionForCat<ErrorCategory::ExceededTimeLimitError>&) {
+ // May throw replacement exception
+ checkForInterrupt();
+ throw;
+ }
+ }
+
+ /**
* Raises a AssertionException if this operation is in a killed state.
*/
void checkForInterrupt() {
@@ -494,15 +572,7 @@ class Interruptible::NotInterruptible final : public Interruptible {
return stdx::cv_status::no_timeout;
}
- try {
- // If the system clock's time_point's compiler-dependent resolution is higher than
- // Date_t's milliseconds, it's possible for the conversion from Date_t to time_point
- // to overflow and trigger an exception. We catch that here to maintain the noexcept
- // contract.
- return cv.wait_until(m, deadline.toSystemTimePoint());
- } catch (const ExceptionFor<ErrorCodes::DurationOverflow>& ex) {
- return ex.toStatus();
- }
+ return cv.wait_until(m, deadline.toSystemTimePoint());
}
Date_t getDeadline() const override {
@@ -513,6 +583,19 @@ class Interruptible::NotInterruptible final : public Interruptible {
return Status::OK();
}
+ // It's invalid to call the deadline or ignore interruption guards on a possibly noop
+ // Interruptible.
+ //
+ // 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;
+ }
+
+ void popIgnoreInterrupts(IgnoreInterruptsState) override {
+ MONGO_UNREACHABLE;
+ }
+
DeadlineState pushArtificialDeadline(Date_t deadline, ErrorCodes::Error error) override {
MONGO_UNREACHABLE;
}