summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2017-01-19 17:25:06 -0500
committerBenety Goh <benety@mongodb.com>2017-01-21 13:00:19 -0500
commitfb028112d3bda109baf9987cfd5986a4475c79bf (patch)
tree52206b1b4bf0e74a5b35e4c59bde468e2c550332
parent962e69b8527543cf6d2f001cd6fa5248d60cc97d (diff)
downloadmongo-fb028112d3bda109baf9987cfd5986a4475c79bf.tar.gz
SERVER-27678 moved DataReplicator::OnCompletionGuard into its own library
(cherry picked from commit 57a0c3d2f7f438d20ce2b078a249c048c7d234d0)
-rw-r--r--src/mongo/db/repl/callback_completion_guard.h150
-rw-r--r--src/mongo/db/repl/data_replicator.cpp40
-rw-r--r--src/mongo/db/repl/data_replicator.h50
3 files changed, 154 insertions, 86 deletions
diff --git a/src/mongo/db/repl/callback_completion_guard.h b/src/mongo/db/repl/callback_completion_guard.h
new file mode 100644
index 00000000000..107205b1809
--- /dev/null
+++ b/src/mongo/db/repl/callback_completion_guard.h
@@ -0,0 +1,150 @@
+/**
+ * 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.
+ */
+
+
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "mongo/stdx/functional.h"
+#include "mongo/stdx/mutex.h"
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+namespace repl {
+
+/**
+ * RAII type that stores the result of callbacks written using the executor::TaskExecutor framework.
+ * Only the first result passed to setResultAndCancelRemainingWork_inlock() is saved.
+ * Calls '_onCompletion' on destruction with result.
+ * We use an invariant to ensure that a result has been provided by the caller at destruction.
+ */
+template <typename Result>
+class CallbackCompletionGuard {
+public:
+ /**
+ * Function to cancel remaining work in caller after setting '_result'.
+ * This function must be called while holding a lock on the caller's mutex.
+ */
+ using CancelRemainingWorkInLockFn = stdx::function<void()>;
+
+ /**
+ * Callback function to pass result to caller at destruction.
+ */
+ typedef stdx::function<void(const Result& result)> OnCompletionFn;
+
+ /**
+ * Constructor for this completion guard.
+ * 'cancelRemainingWorkInLock' is called after setting the result to cancel any outstanding
+ * work in the caller. 'cancelRemainingWorkInLock' must be called while holding a lock on the
+ * caller's mutex.
+ * 'onCompletion' is called with the result at destruction.
+ */
+ CallbackCompletionGuard(const CancelRemainingWorkInLockFn& cancelRemainingWorkInLock,
+ const OnCompletionFn& onCompletion);
+
+ /**
+ * Invokes '_onCompletion' with the result.
+ * Aborts if:
+ * result is not set; or
+ * '_onCompletion' throws an exception.
+ */
+ ~CallbackCompletionGuard();
+
+ /**
+ * Sets result if called for the first time.
+ * Cancels remaining work in caller.
+ * Requires either a unique_lock or lock_guard to be passed in to ensure that we call
+ * _cancelRemainingWork_inlock()) while we have a lock on the callers's mutex.
+ */
+ void setResultAndCancelRemainingWork_inlock(const stdx::lock_guard<stdx::mutex>& lock,
+ const Result& result);
+ void setResultAndCancelRemainingWork_inlock(const stdx::unique_lock<stdx::mutex>& lock,
+ const Result& result);
+
+private:
+ /**
+ * Once we verified that we have the caller's lock, this function is called by both
+ * versions of setResultAndCancelRemainingWork_inlock() to set the result and cancel any
+ * remaining work in the caller.
+ */
+ void _setResultAndCancelRemainingWork_inlock(const Result& result);
+
+ // Called at most once after setting '_result'.
+ const CancelRemainingWorkInLockFn _cancelRemainingWorkInLock;
+
+ // Called at destruction with '_result'.
+ const OnCompletionFn _onCompletion;
+
+ // _result is guarded by the mutex of the caller instance that owns this guard object.
+ boost::optional<Result> _result;
+};
+
+template <typename Result>
+CallbackCompletionGuard<Result>::CallbackCompletionGuard(
+ const CancelRemainingWorkInLockFn& cancelRemainingWorkInLock,
+ const OnCompletionFn& onCompletion)
+ : _cancelRemainingWorkInLock(cancelRemainingWorkInLock), _onCompletion(onCompletion) {}
+
+template <typename Result>
+CallbackCompletionGuard<Result>::~CallbackCompletionGuard() {
+ invariant(_result);
+
+ // _onCompletion() must be called outside the caller's lock to avoid a deadlock.
+ // If '_onCompletion' throws an exception, the exception will be logged (by the terminate hook)
+ // and the program will abort.
+ _onCompletion(*_result);
+}
+
+template <typename Result>
+void CallbackCompletionGuard<Result>::setResultAndCancelRemainingWork_inlock(
+ const stdx::lock_guard<stdx::mutex>& lock, const Result& result) {
+ _setResultAndCancelRemainingWork_inlock(result);
+}
+
+template <typename Result>
+void CallbackCompletionGuard<Result>::setResultAndCancelRemainingWork_inlock(
+ const stdx::unique_lock<stdx::mutex>& lock, const Result& result) {
+ invariant(lock.owns_lock());
+ _setResultAndCancelRemainingWork_inlock(result);
+}
+
+template <typename Result>
+void CallbackCompletionGuard<Result>::_setResultAndCancelRemainingWork_inlock(
+ const Result& result) {
+ if (_result) {
+ return;
+ }
+ _result = result;
+
+ // This is called at most once.
+ _cancelRemainingWorkInLock();
+}
+
+} // namespace repl
+} // namespace mongo
diff --git a/src/mongo/db/repl/data_replicator.cpp b/src/mongo/db/repl/data_replicator.cpp
index 0fb71dacd75..f645e6a385a 100644
--- a/src/mongo/db/repl/data_replicator.cpp
+++ b/src/mongo/db/repl/data_replicator.cpp
@@ -1497,46 +1497,6 @@ Status DataReplicator::_enqueueDocuments(Fetcher::Documents::const_iterator begi
return Status::OK();
}
-DataReplicator::OnCompletionGuard::OnCompletionGuard(
- const CancelRemainingWorkInLockFn& cancelRemainingWorkInLock,
- const OnCompletionFn& onCompletion)
- : _cancelRemainingWorkInLock(cancelRemainingWorkInLock), _onCompletion(onCompletion) {}
-
-DataReplicator::OnCompletionGuard::~OnCompletionGuard() {
- MONGO_DESTRUCTOR_GUARD({
- if (!_lastAppliedSet) {
- severe() << "It is a programming error to destroy this initial sync attempt completion "
- "guard without the caller providing a result for '_lastApplied'";
- }
- invariant(_lastAppliedSet);
- // _onCompletion() must be called outside the DataReplicator's lock to avoid a deadlock.
- _onCompletion(_lastApplied);
- });
-}
-
-void DataReplicator::OnCompletionGuard::setResultAndCancelRemainingWork_inlock(
- const stdx::lock_guard<stdx::mutex>&, const StatusWith<OpTimeWithHash>& lastApplied) {
- _setResultAndCancelRemainingWork_inlock(lastApplied);
-}
-
-void DataReplicator::OnCompletionGuard::setResultAndCancelRemainingWork_inlock(
- const stdx::unique_lock<stdx::mutex>& lock, const StatusWith<OpTimeWithHash>& lastApplied) {
- invariant(lock.owns_lock());
- _setResultAndCancelRemainingWork_inlock(lastApplied);
-}
-
-void DataReplicator::OnCompletionGuard::_setResultAndCancelRemainingWork_inlock(
- const StatusWith<OpTimeWithHash>& lastApplied) {
- if (_lastAppliedSet) {
- return;
- }
- _lastApplied = lastApplied;
- _lastAppliedSet = true;
-
- // It is fine to call this multiple times.
- _cancelRemainingWorkInLock();
-}
-
std::string DataReplicator::Stats::toString() const {
return toBSON().toString();
}
diff --git a/src/mongo/db/repl/data_replicator.h b/src/mongo/db/repl/data_replicator.h
index 4ee9c3082e9..7a7cd96512f 100644
--- a/src/mongo/db/repl/data_replicator.h
+++ b/src/mongo/db/repl/data_replicator.h
@@ -37,6 +37,7 @@
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/timestamp.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/db/repl/callback_completion_guard.h"
#include "mongo/db/repl/collection_cloner.h"
#include "mongo/db/repl/data_replicator_external_state.h"
#include "mongo/db/repl/multiapplier.h"
@@ -155,52 +156,9 @@ public:
typedef stdx::function<void(const StatusWith<OpTimeWithHash>& lastApplied)> OnCompletionFn;
/**
- * RAII type that stores the result of a single initial sync attempt.
- * Only the first result passed to setResultAndCancelRemainingWork_inlock() is saved.
- * Calls '_onCompletion' on destruction with result.
- * We use an invariant to ensure that a result has been provided by the caller at destruction.
- */
- class OnCompletionGuard {
- MONGO_DISALLOW_COPYING(OnCompletionGuard);
-
- public:
- // Function to invoke DataReplicator::_cancelRemainingWork_inlock().
- using CancelRemainingWorkInLockFn = stdx::function<void()>;
-
- OnCompletionGuard(const CancelRemainingWorkInLockFn& cancelRemainingWorkInLock,
- const OnCompletionFn& onCompletion);
- ~OnCompletionGuard();
-
- /**
- * Sets result if called for the first time.
- * Cancels remaining work in DataReplicator.
- * Requires either a unique_lock or lock_guard to be passed in to ensure that we call
- * DataReplicator::_cancelRemainingWork_inlock()) while we have a lock on the data
- * replicator's mutex.
- */
- void setResultAndCancelRemainingWork_inlock(const stdx::lock_guard<stdx::mutex>& lock,
- const StatusWith<OpTimeWithHash>& lastApplied);
- void setResultAndCancelRemainingWork_inlock(const stdx::unique_lock<stdx::mutex>& lock,
- const StatusWith<OpTimeWithHash>& lastApplied);
-
- private:
- /**
- * Once we verified that we have the data replicator lock, this function is called by both
- * versions of setResultAndCancelRemainingWork_inlock() to set the result and cancel any
- * remaining work in the data replicator.
- */
- void _setResultAndCancelRemainingWork_inlock(const StatusWith<OpTimeWithHash>& lastApplied);
-
- const CancelRemainingWorkInLockFn _cancelRemainingWorkInLock;
- const OnCompletionFn _onCompletion;
-
- // _lastAppliedSet and _lastApplied are guarded by the mutex of the DataReplicator instance
- // that owns this guard object.
- bool _lastAppliedSet = false;
- StatusWith<OpTimeWithHash> _lastApplied =
- Status(ErrorCodes::InternalError,
- "This initial sync attempt finished without an explicit result.");
- };
+ * Callback completion guard for data replicator.
+ */
+ using OnCompletionGuard = CallbackCompletionGuard<StatusWith<OpTimeWithHash>>;
struct InitialSyncAttemptInfo {
int durationMillis;