diff options
author | Benety Goh <benety@mongodb.com> | 2017-01-19 17:25:06 -0500 |
---|---|---|
committer | Benety Goh <benety@mongodb.com> | 2017-01-21 13:00:19 -0500 |
commit | fb028112d3bda109baf9987cfd5986a4475c79bf (patch) | |
tree | 52206b1b4bf0e74a5b35e4c59bde468e2c550332 | |
parent | 962e69b8527543cf6d2f001cd6fa5248d60cc97d (diff) | |
download | mongo-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.h | 150 | ||||
-rw-r--r-- | src/mongo/db/repl/data_replicator.cpp | 40 | ||||
-rw-r--r-- | src/mongo/db/repl/data_replicator.h | 50 |
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; |