summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Stearn <mathias@10gen.com>2017-03-21 18:16:08 -0400
committerMathias Stearn <mathias@10gen.com>2017-03-22 19:15:21 -0400
commit86bc5bdac397909e246f0ea19f5414387bb6b0a9 (patch)
treec99a95052141d4749fd24d22007ee60768feb087
parent51d8b9c2f5eafc457f889d9786ebd68e4398ba64 (diff)
downloadmongo-86bc5bdac397909e246f0ea19f5414387bb6b0a9.tar.gz
SERVER-28421 Implement ClockSource::waitForConditionUntil()
-rw-r--r--src/mongo/db/operation_context.cpp56
-rw-r--r--src/mongo/util/SConscript1
-rw-r--r--src/mongo/util/clock_source.cpp87
-rw-r--r--src/mongo/util/clock_source.h39
4 files changed, 128 insertions, 55 deletions
diff --git a/src/mongo/db/operation_context.cpp b/src/mongo/db/operation_context.cpp
index 0950c620280..4170797da1e 100644
--- a/src/mongo/db/operation_context.cpp
+++ b/src/mongo/db/operation_context.cpp
@@ -245,53 +245,6 @@ stdx::cv_status OperationContext::waitForConditionOrInterruptUntil(
return uassertStatusOK(waitForConditionOrInterruptNoAssertUntil(cv, m, deadline));
}
-static NOINLINE_DECL stdx::cv_status cvWaitUntilWithClockSource(ClockSource* clockSource,
- stdx::condition_variable& cv,
- stdx::unique_lock<stdx::mutex>& m,
- Date_t deadline) {
- if (deadline <= clockSource->now()) {
- return stdx::cv_status::timeout;
- }
-
- struct AlarmInfo {
- stdx::mutex controlMutex;
- stdx::mutex* waitMutex;
- stdx::condition_variable* waitCV;
- stdx::cv_status cvWaitResult = stdx::cv_status::no_timeout;
- };
- auto alarmInfo = std::make_shared<AlarmInfo>();
- alarmInfo->waitCV = &cv;
- alarmInfo->waitMutex = m.mutex();
- const auto waiterThreadId = stdx::this_thread::get_id();
- bool invokedAlarmInline = false;
- invariantOK(clockSource->setAlarm(deadline, [alarmInfo, waiterThreadId, &invokedAlarmInline] {
- stdx::lock_guard<stdx::mutex> controlLk(alarmInfo->controlMutex);
- alarmInfo->cvWaitResult = stdx::cv_status::timeout;
- if (!alarmInfo->waitMutex) {
- return;
- }
- if (stdx::this_thread::get_id() == waiterThreadId) {
- // In NetworkInterfaceMock, setAlarm may invoke its callback immediately if the deadline
- // has expired, so we detect that case and avoid self-deadlock by returning early, here.
- // It is safe to set invokedAlarmInline without synchronization in this case, because it
- // is exactly the case where the same thread is writing and consulting the value.
- invokedAlarmInline = true;
- return;
- }
- stdx::lock_guard<stdx::mutex> waitLk(*alarmInfo->waitMutex);
- alarmInfo->waitCV->notify_all();
- }));
- if (!invokedAlarmInline) {
- cv.wait(m);
- }
- m.unlock();
- stdx::lock_guard<stdx::mutex> controlLk(alarmInfo->controlMutex);
- m.lock();
- alarmInfo->waitMutex = nullptr;
- alarmInfo->waitCV = nullptr;
- return alarmInfo->cvWaitResult;
-}
-
// Theory of operation for waitForConditionOrInterruptNoAssertUntil and markKilled:
//
// An operation indicates to potential killers that it is waiting on a condition variable by setting
@@ -344,14 +297,7 @@ StatusWith<stdx::cv_status> OperationContext::waitForConditionOrInterruptNoAsser
cv.wait(m);
return stdx::cv_status::no_timeout;
}
- const auto clockSource = getServiceContext()->getPreciseClockSource();
- if (clockSource->tracksSystemClock()) {
- return cv.wait_until(m, deadline.toSystemTimePoint());
- }
-
- // The following cases only occur during testing, when the precise clock source is
- // virtualized and does not track the system clock.
- return cvWaitUntilWithClockSource(clockSource, cv, m, deadline);
+ return getServiceContext()->getPreciseClockSource()->waitForConditionUntil(cv, m, deadline);
}();
// Continue waiting on cv until no other thread is attempting to kill this one.
diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript
index 51e71144e3d..f555dcfcd3e 100644
--- a/src/mongo/util/SConscript
+++ b/src/mongo/util/SConscript
@@ -343,6 +343,7 @@ env.Library(
target='clock_sources',
source=[
'background_thread_clock_source.cpp',
+ 'clock_source.cpp',
'fast_clock_source_factory.cpp',
],
LIBDEPS=[
diff --git a/src/mongo/util/clock_source.cpp b/src/mongo/util/clock_source.cpp
new file mode 100644
index 00000000000..c8e70537dbc
--- /dev/null
+++ b/src/mongo/util/clock_source.cpp
@@ -0,0 +1,87 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/stdx/thread.h"
+#include "mongo/util/clock_source.h"
+
+namespace mongo {
+stdx::cv_status ClockSource::waitForConditionUntil(stdx::condition_variable& cv,
+ stdx::unique_lock<stdx::mutex>& m,
+ Date_t deadline) {
+ if (_tracksSystemClock) {
+ return cv.wait_until(m, deadline.toSystemTimePoint());
+ }
+
+ // The rest of this function only runs during testing, when the clock source is virtualized and
+ // does not track the system clock.
+
+ if (deadline <= now()) {
+ return stdx::cv_status::timeout;
+ }
+
+ struct AlarmInfo {
+ stdx::mutex controlMutex;
+ stdx::mutex* waitMutex;
+ stdx::condition_variable* waitCV;
+ stdx::cv_status cvWaitResult = stdx::cv_status::no_timeout;
+ };
+ auto alarmInfo = std::make_shared<AlarmInfo>();
+ alarmInfo->waitCV = &cv;
+ alarmInfo->waitMutex = m.mutex();
+ const auto waiterThreadId = stdx::this_thread::get_id();
+ bool invokedAlarmInline = false;
+ invariantOK(setAlarm(deadline, [alarmInfo, waiterThreadId, &invokedAlarmInline] {
+ stdx::lock_guard<stdx::mutex> controlLk(alarmInfo->controlMutex);
+ alarmInfo->cvWaitResult = stdx::cv_status::timeout;
+ if (!alarmInfo->waitMutex) {
+ return;
+ }
+ if (stdx::this_thread::get_id() == waiterThreadId) {
+ // In NetworkInterfaceMock, setAlarm may invoke its callback immediately if the deadline
+ // has expired, so we detect that case and avoid self-deadlock by returning early, here.
+ // It is safe to set invokedAlarmInline without synchronization in this case, because it
+ // is exactly the case where the same thread is writing and consulting the value.
+ invokedAlarmInline = true;
+ return;
+ }
+ stdx::lock_guard<stdx::mutex> waitLk(*alarmInfo->waitMutex);
+ alarmInfo->waitCV->notify_all();
+ }));
+ if (!invokedAlarmInline) {
+ cv.wait(m);
+ }
+ m.unlock();
+ stdx::lock_guard<stdx::mutex> controlLk(alarmInfo->controlMutex);
+ m.lock();
+ alarmInfo->waitMutex = nullptr;
+ alarmInfo->waitCV = nullptr;
+ return alarmInfo->cvWaitResult;
+}
+} // namespace mongo
diff --git a/src/mongo/util/clock_source.h b/src/mongo/util/clock_source.h
index c8f8d637f81..e38ce7c932f 100644
--- a/src/mongo/util/clock_source.h
+++ b/src/mongo/util/clock_source.h
@@ -28,7 +28,9 @@
#pragma once
+#include "mongo/stdx/condition_variable.h"
#include "mongo/stdx/functional.h"
+#include "mongo/stdx/mutex.h"
#include "mongo/util/time_support.h"
namespace mongo {
@@ -71,6 +73,43 @@ public:
return _tracksSystemClock;
}
+ /**
+ * Like cv.wait_until(m, deadline), but uses this ClockSource instead of
+ * stdx::chrono::system_clock to measure the passage of time.
+ */
+ stdx::cv_status waitForConditionUntil(stdx::condition_variable& cv,
+ stdx::unique_lock<stdx::mutex>& m,
+ Date_t deadline);
+
+ /**
+ * Like cv.wait_until(m, deadline, pred), but uses this ClockSource instead of
+ * stdx::chrono::system_clock to measure the passage of time.
+ */
+ template <typename Pred>
+ bool waitForConditionUntil(stdx::condition_variable& cv,
+ stdx::unique_lock<stdx::mutex>& m,
+ Date_t deadline,
+ const Pred& pred) {
+ while (!pred()) {
+ if (waitForConditionUntil(cv, m, deadline) == stdx::cv_status::timeout) {
+ return pred();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Like cv.wait_for(m, duration, pred), but uses this ClockSource instead of
+ * stdx::chrono::system_clock to measure the passage of time.
+ */
+ template <typename Duration, typename Pred>
+ bool waitForConditionFor(stdx::condition_variable& cv,
+ stdx::unique_lock<stdx::mutex>& m,
+ Duration duration,
+ const Pred& pred) {
+ return waitForConditionUntil(cv, m, now() + duration, pred);
+ }
+
protected:
bool _tracksSystemClock = true;
};