diff options
Diffstat (limited to 'src/mongo/util')
-rw-r--r-- | src/mongo/util/diagnostic_info.cpp | 2 | ||||
-rw-r--r-- | src/mongo/util/diagnostic_info.h | 2 | ||||
-rw-r--r-- | src/mongo/util/fail_point.cpp | 28 | ||||
-rw-r--r-- | src/mongo/util/fail_point.h | 309 | ||||
-rw-r--r-- | src/mongo/util/fail_point_registry.cpp | 43 | ||||
-rw-r--r-- | src/mongo/util/fail_point_registry.h | 6 | ||||
-rw-r--r-- | src/mongo/util/fail_point_service.cpp | 35 | ||||
-rw-r--r-- | src/mongo/util/fail_point_service.h | 28 | ||||
-rw-r--r-- | src/mongo/util/fail_point_test.cpp | 74 | ||||
-rw-r--r-- | src/mongo/util/net/sock.cpp | 6 | ||||
-rw-r--r-- | src/mongo/util/net/sock_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/util/net/ssl/detail/io.hpp | 4 |
12 files changed, 254 insertions, 285 deletions
diff --git a/src/mongo/util/diagnostic_info.cpp b/src/mongo/util/diagnostic_info.cpp index a42cc5275a9..a05dcb8a3f6 100644 --- a/src/mongo/util/diagnostic_info.cpp +++ b/src/mongo/util/diagnostic_info.cpp @@ -73,7 +73,7 @@ MONGO_INITIALIZER(LockActions)(InitializerContext* context) { DiagnosticInfo::Diagnostic::clearDiagnostic(); } void onFailedLock() override { - if (!MONGO_FAIL_POINT(keepDiagnosticCaptureOnFailedLock)) { + if (!MONGO_unlikely(keepDiagnosticCaptureOnFailedLock.shouldFail())) { DiagnosticInfo::Diagnostic::clearDiagnostic(); } } diff --git a/src/mongo/util/diagnostic_info.h b/src/mongo/util/diagnostic_info.h index 7f7325c9b23..0a4958f35de 100644 --- a/src/mongo/util/diagnostic_info.h +++ b/src/mongo/util/diagnostic_info.h @@ -36,7 +36,7 @@ #include "mongo/util/time_support.h" namespace mongo { -MONGO_FAIL_POINT_DECLARE(keepDiagnosticCaptureOnFailedLock); +extern FailPoint keepDiagnosticCaptureOnFailedLock; /** * DiagnosticInfo keeps track of diagnostic information such as a developer provided * name, the time when a lock was first acquired, and a partial caller call stack. diff --git a/src/mongo/util/fail_point.cpp b/src/mongo/util/fail_point.cpp index d5af18f318f..c40a4b829d6 100644 --- a/src/mongo/util/fail_point.cpp +++ b/src/mongo/util/fail_point.cpp @@ -84,7 +84,7 @@ void FailPoint::shouldFailCloseBlock() { _fpInfo.subtractAndFetch(1); } -void FailPoint::setMode(Mode mode, ValType val, const BSONObj& extra) { +void FailPoint::setMode(Mode mode, ValType val, BSONObj extra) { /** * Outline: * @@ -96,20 +96,20 @@ void FailPoint::setMode(Mode mode, ValType val, const BSONObj& extra) { stdx::lock_guard<stdx::mutex> scoped(_modMutex); // Step 1 - disableFailPoint(); + disable(); // Step 2 while (_fpInfo.load() != 0) { sleepmillis(50); } + // Step 3 _mode = mode; _timesOrPeriod.store(val); - - _data = extra.copy(); + _data = std::move(extra); if (_mode != off) { - enableFailPoint(); + enable(); } } @@ -117,19 +117,19 @@ const BSONObj& FailPoint::getData() const { return _data; } -void FailPoint::enableFailPoint() { - _fpInfo.fetchAndBitOr(ACTIVE_BIT); +void FailPoint::enable() { + _fpInfo.fetchAndBitOr(kActiveBit); } -void FailPoint::disableFailPoint() { - _fpInfo.fetchAndBitAnd(~ACTIVE_BIT); +void FailPoint::disable() { + _fpInfo.fetchAndBitAnd(~kActiveBit); } FailPoint::RetCode FailPoint::slowShouldFailOpenBlock( std::function<bool(const BSONObj&)> cb) noexcept { ValType localFpInfo = _fpInfo.addAndFetch(1); - if ((localFpInfo & ACTIVE_BIT) == 0) { + if ((localFpInfo & kActiveBit) == 0) { return slowOff; } @@ -149,7 +149,7 @@ FailPoint::RetCode FailPoint::slowShouldFailOpenBlock( } case nTimes: { if (_timesOrPeriod.subtractAndFetch(1) <= 0) - disableFailPoint(); + disable(); return slowOn; } @@ -168,8 +168,7 @@ FailPoint::RetCode FailPoint::slowShouldFailOpenBlock( } } -StatusWith<std::tuple<FailPoint::Mode, FailPoint::ValType, BSONObj>> FailPoint::parseBSON( - const BSONObj& obj) { +StatusWith<FailPoint::ModeOptions> FailPoint::parseBSON(const BSONObj& obj) { Mode mode = FailPoint::alwaysOn; ValType val = 0; const BSONElement modeElem(obj["mode"]); @@ -177,7 +176,6 @@ StatusWith<std::tuple<FailPoint::Mode, FailPoint::ValType, BSONObj>> FailPoint:: return {ErrorCodes::IllegalOperation, "When setting a failpoint, you must supply a 'mode'"}; } else if (modeElem.type() == String) { const std::string modeStr(modeElem.valuestr()); - if (modeStr == "off") { mode = FailPoint::off; } else if (modeStr == "alwaysOn") { @@ -255,7 +253,7 @@ StatusWith<std::tuple<FailPoint::Mode, FailPoint::ValType, BSONObj>> FailPoint:: data = obj["data"].Obj().getOwned(); } - return std::make_tuple(mode, val, data); + return ModeOptions{mode, val, data}; } BSONObj FailPoint::toBSON() const { diff --git a/src/mongo/util/fail_point.h b/src/mongo/util/fail_point.h index f82d85785a5..57ee76bca9d 100644 --- a/src/mongo/util/fail_point.h +++ b/src/mongo/util/fail_point.h @@ -44,22 +44,27 @@ namespace mongo { * deactivated, as well as embed temporary data into it. * * The fail point has a static instance, which is represented by a FailPoint - * object, and dynamic instance, which are all the threads in between - * shouldFailOpenBlock and shouldFailCloseBlock. + * object, and dynamic instance, represented by FailPoint::Scoped handles. * * Sample use: * // Declared somewhere: * FailPoint makeBadThingsHappen; * * // Somewhere in the code - * return false || MONGO_FAIL_POINT(makeBadThingsHappen); + * return false || MONGO_unlikely(makeBadThingsHappen.shouldFail()); * * or * * // Somewhere in the code - * MONGO_FAIL_POINT_BLOCK(makeBadThingsHappen, blockMakeBadThingsHappen) { - * const BSONObj& data = blockMakeBadThingsHappen.getData(); + * makeBadThingsHappen.execute([&](const BSONObj& data) { * // Do something + * }); + * + * // Another way to do it, where lambda isn't suitable, e.g. to cause an early return + * // of the enclosing function. + * if (auto sfp = makeBadThingsHappen.scoped(); MONGO_unlikely(sfp.isActive())) { + * const BSONObj& data = sfp.getData(); + * // Do something, including break, continue, return, etc... * } * * Invariants: @@ -69,13 +74,65 @@ namespace mongo { * 2. Client visible fail point states are read-only when active. */ class FailPoint { - FailPoint(const FailPoint&) = delete; - FailPoint& operator=(const FailPoint&) = delete; +private: + enum RetCode { fastOff = 0, slowOff, slowOn, userIgnored }; public: - typedef unsigned ValType; + using ValType = unsigned; enum Mode { off, alwaysOn, random, nTimes, skip }; - enum RetCode { fastOff = 0, slowOff, slowOn, userIgnored }; + + struct ModeOptions { + Mode mode; + ValType val; + BSONObj extra; + }; + + /** + * Helper class for making sure that FailPoint#shouldFailCloseBlock is called when + * FailPoint#shouldFailOpenBlock was called. + * + * Users don't create these. They are only used within the execute and executeIf + * functions and returned by the scoped() and scopedIf() functions. + */ + class Scoped { + public: + Scoped(FailPoint* failPoint, RetCode ret) + : _failPoint(failPoint), + _active(ret == FailPoint::slowOn), + _holdsRef(ret != FailPoint::fastOff) {} + + ~Scoped() { + if (_holdsRef) { + _failPoint->shouldFailCloseBlock(); + } + } + + Scoped(const Scoped&) = delete; + Scoped& operator=(const Scoped&) = delete; + + /** + * @return true if fail point is on. + * Calls to isActive should be placed inside MONGO_unlikely for performance. + */ + bool isActive() { + return _active; + } + + /** + * @return the data stored in the fail point. #isActive must be true + * before you can call this. + */ + const BSONObj& getData() const { + // Assert when attempting to get data without holding a ref. + fassert(16445, _holdsRef); + return _failPoint->getData(); + } + + private: + FailPoint* _failPoint; + bool _active; + bool _holdsRef; + }; /** * Explicitly resets the seed used for the PRNG in this thread. If not called on a thread, @@ -84,21 +141,26 @@ public: static void setThreadPRNGSeed(int32_t seed); /** - * Parses the FailPoint::Mode, FailPoint::ValType, and data BSONObj from the BSON. + * Parses the {Mode, ValType, BSONObj} from the BSON. */ - static StatusWith<std::tuple<Mode, ValType, BSONObj>> parseBSON(const BSONObj& obj); + static StatusWith<ModeOptions> parseBSON(const BSONObj& obj); FailPoint(); + FailPoint(const FailPoint&) = delete; + FailPoint& operator=(const FailPoint&) = delete; + /** * Note: This is not side-effect free - it can change the state to OFF after calling. - * Note: see MONGO_FAIL_POINT_BLOCK_IF for information on the passed callable + * Note: see `executeIf` for information on `pred`. + * + * Calls to shouldFail should be placed inside MONGO_unlikely for performance. * * @return true if fail point is active. */ - template <typename Callable = std::nullptr_t> - inline bool shouldFail(Callable&& cb = nullptr) { - RetCode ret = shouldFailOpenBlock(std::forward<Callable>(cb)); + template <typename Pred> + bool shouldFail(Pred&& pred) { + RetCode ret = shouldFailOpenBlock(std::forward<Pred>(pred)); if (MONGO_likely(ret == fastOff)) { return false; @@ -108,34 +170,11 @@ public: return ret == slowOn; } - /** - * Checks whether fail point is active and increments the reference counter without - * decrementing it. Must call shouldFailCloseBlock afterwards when the return value - * is not fastOff. Otherwise, this will remain read-only forever. - * - * Note: see MONGO_FAIL_POINT_BLOCK_IF for information on the passed callable - * - * @return slowOn if its active and needs to be closed - * userIgnored if its active and needs to be closed, but shouldn't be acted on - * slowOff if its disabled and needs to be closed - * fastOff if its disabled and doesn't need to be closed - */ - template <typename Callable = std::nullptr_t> - inline RetCode shouldFailOpenBlock(Callable&& cb = nullptr) { - if (MONGO_likely((_fpInfo.loadRelaxed() & ACTIVE_BIT) == 0)) { - return fastOff; - } - - return slowShouldFailOpenBlock(std::forward<Callable>(cb)); + bool shouldFail() { + return shouldFail(nullptr); } /** - * Decrements the reference counter. - * @see #shouldFailOpenBlock - */ - void shouldFailCloseBlock(); - - /** * Changes the settings of this fail point. This will turn off the fail point * and waits for all dynamic instances referencing this fail point to go away before * actually modifying the settings. @@ -157,143 +196,125 @@ public: * that can be referenced afterwards with #getData. Defaults to an empty * document. */ - void setMode(Mode mode, ValType val = 0, const BSONObj& extra = BSONObj()); + void setMode(Mode mode, ValType val = 0, BSONObj extra = {}); + void setMode(ModeOptions opt) { + setMode(std::move(opt.mode), std::move(opt.val), std::move(opt.extra)); + } /** * @returns a BSON object showing the current mode and data stored. */ BSONObj toBSON() const; -private: - static const ValType ACTIVE_BIT = 1 << 31; - static const ValType REF_COUNTER_MASK = ~ACTIVE_BIT; - - // Bit layout: - // 31: tells whether this fail point is active. - // 0~30: unsigned ref counter for active dynamic instances. - AtomicWord<unsigned> _fpInfo{0}; - - // Invariant: These should be read only if ACTIVE_BIT of _fpInfo is set. - Mode _mode{off}; - AtomicWord<int> _timesOrPeriod{0}; - BSONObj _data; - - // protects _mode, _timesOrPeriod, _data - mutable stdx::mutex _modMutex; - /** - * Enables this fail point. + * Create a Scoped from this FailPoint. + * Use the Scoped object to access failpoint data. */ - void enableFailPoint(); - + Scoped scoped() { + return scopedIf(nullptr); + } /** - * Disables this fail point. + * Create a Scoped from this FailPoint, only active when `pred(payload)` is true. + * See `executeIf`. Use the Scoped object to access failpoint data. */ - void disableFailPoint(); + template <typename Pred> + Scoped scopedIf(Pred&& pred) { + return Scoped(this, shouldFailOpenBlock(std::forward<Pred>(pred))); + } - /** - * slow path for #shouldFailOpenBlock - * - * If a callable is passed, and returns false, this will return userIgnored and avoid altering - * the mode in any way. The argument is the fail point payload. - */ - RetCode slowShouldFailOpenBlock(std::function<bool(const BSONObj&)> cb) noexcept; + template <typename F> + void execute(F&& f) { + return executeIf(f, nullptr); + } /** - * @return the stored BSONObj in this fail point. Note that this cannot be safely - * read if this fail point is off. + * The predicate `pred` should behave like a `bool pred(const BSONObj& payload)`. + * If `pred(payload)`, then `f(payload)` is executed. Otherwise, `f` is not + * executed and this FailPoint's mode is not altered (e.g. `nTimes` isn't consumed). */ - const BSONObj& getData() const; - - friend class ScopedFailPoint; -}; - -/** - * Helper class for making sure that FailPoint#shouldFailCloseBlock is called when - * FailPoint#shouldFailOpenBlock was called. This should only be used within the - * MONGO_FAIL_POINT_BLOCK macro. - */ -class ScopedFailPoint { - ScopedFailPoint(const ScopedFailPoint&) = delete; - ScopedFailPoint& operator=(const ScopedFailPoint&) = delete; + template <typename F, typename Pred> + void executeIf(F&& f, Pred&& pred) { + auto sfp = scopedIf(std::forward<Pred>(pred)); + if (MONGO_unlikely(sfp.isActive())) { + std::forward<F>(f)(sfp.getData()); + } + } -public: - template <typename Callable = std::nullptr_t> - ScopedFailPoint(FailPoint* failPoint, Callable&& cb = nullptr) : _failPoint(failPoint) { - FailPoint::RetCode ret = _failPoint->shouldFailOpenBlock(std::forward<Callable>(cb)); - _shouldClose = ret != FailPoint::fastOff; - _shouldRun = ret == FailPoint::slowOn; + void pauseWhileSet() { + while (MONGO_unlikely(shouldFail())) { + sleepmillis(100); + } } - ~ScopedFailPoint() { - if (_shouldClose) { - _failPoint->shouldFailCloseBlock(); + void pauseWhileSet(OperationContext* opCtx) { + while (MONGO_unlikely(shouldFail())) { + opCtx->sleepFor(Milliseconds(100)); } } +private: + void enable(); + void disable(); + /** - * @return true if fail point is on. This will be true at most once. + * Checks whether fail point is active and increments the reference counter without + * decrementing it. Must call shouldFailCloseBlock afterwards when the return value + * is not fastOff. Otherwise, this will remain read-only forever. + * + * Note: see `executeIf` for information on `pred`. + * + * @return slowOn if its active and needs to be closed + * userIgnored if its active and needs to be closed, but shouldn't be acted on + * slowOff if its disabled and needs to be closed + * fastOff if its disabled and doesn't need to be closed */ - inline bool isActive() { - if (!_shouldRun) { - return false; + template <typename Pred> + RetCode shouldFailOpenBlock(Pred&& pred) { + if (MONGO_likely((_fpInfo.loadRelaxed() & kActiveBit) == 0)) { + return fastOff; } - // We use this in a for loop to prevent iteration, thus flipping to inactive after the first - // time. - _shouldRun = false; - return true; + return slowShouldFailOpenBlock(std::forward<Pred>(pred)); + } + + RetCode shouldFailOpenBlock() { + return shouldFailOpenBlock(nullptr); } /** - * @return the data stored in the fail point. #isActive must be true - * before you can call this. + * Decrements the reference counter. + * @see #shouldFailOpenBlock */ - const BSONObj& getData() const { - // Assert when attempting to get data without incrementing ref counter. - fassert(16445, _shouldClose); - return _failPoint->getData(); - } + void shouldFailCloseBlock(); -private: - FailPoint* _failPoint; - bool _shouldRun; - bool _shouldClose; -}; + /** + * slow path for #shouldFailOpenBlock + * + * If a callable is passed, and returns false, this will return userIgnored and avoid altering + * the mode in any way. The argument is the fail point payload. + */ + RetCode slowShouldFailOpenBlock(std::function<bool(const BSONObj&)> cb) noexcept; -#define MONGO_FAIL_POINT(symbol) MONGO_unlikely(symbol.shouldFail()) + /** + * @return the stored BSONObj in this fail point. Note that this cannot be safely + * read if this fail point is off. + */ + const BSONObj& getData() const; -inline void MONGO_FAIL_POINT_PAUSE_WHILE_SET(FailPoint& failPoint) { - while (MONGO_FAIL_POINT(failPoint)) { - sleepmillis(100); - } -} + static const ValType kActiveBit = 1 << 31; -inline void MONGO_FAIL_POINT_PAUSE_WHILE_SET_OR_INTERRUPTED(OperationContext* opCtx, - FailPoint& failPoint) { - while (MONGO_FAIL_POINT(failPoint)) { - opCtx->sleepFor(Milliseconds(100)); - } -} + // Bit layout: + // 31: tells whether this fail point is active. + // 0~30: unsigned ref counter for active dynamic instances. + AtomicWord<std::uint32_t> _fpInfo{0}; -/** - * Macro for creating a fail point with block context. Also use this when - * you want to access the data stored in the fail point. - */ -#define MONGO_FAIL_POINT_BLOCK(symbol, blockSymbol) \ - for (mongo::ScopedFailPoint blockSymbol(&symbol); MONGO_unlikely(blockSymbol.isActive());) + // Invariant: These should be read only if kActiveBit of _fpInfo is set. + Mode _mode{off}; + AtomicWord<int> _timesOrPeriod{0}; + BSONObj _data; -/** - * Macro for creating a fail point with block context and a pre-flight condition. Also use this when - * you want to access the data stored in the fail point. - * - * Your passed in callable should take a const BSONObj& (the fail point payload) and return bool. - * If it returns true, you'll process the block as normal. If you return false, you'll exit the - * block without evaluating it and avoid altering the mode in any way (you won't consume nTimes for - * instance). - */ -#define MONGO_FAIL_POINT_BLOCK_IF(symbol, blockSymbol, ...) \ - for (mongo::ScopedFailPoint blockSymbol(&symbol, __VA_ARGS__); \ - MONGO_unlikely(blockSymbol.isActive());) + // protects _mode, _timesOrPeriod, _data + mutable stdx::mutex _modMutex; +}; } // namespace mongo diff --git a/src/mongo/util/fail_point_registry.cpp b/src/mongo/util/fail_point_registry.cpp index 158d6ab8023..f4dfb627a36 100644 --- a/src/mongo/util/fail_point_registry.cpp +++ b/src/mongo/util/fail_point_registry.cpp @@ -29,34 +29,32 @@ #include "mongo/util/fail_point_registry.h" +#include <fmt/format.h> + #include "mongo/bson/json.h" #include "mongo/util/fail_point_server_parameter_gen.h" #include "mongo/util/fail_point_service.h" -#include "mongo/util/map_util.h" -#include "mongo/util/str.h" namespace mongo { -constexpr auto kFailPointServerParameterPrefix = "failpoint."_sd; +using namespace fmt::literals; FailPointRegistry::FailPointRegistry() : _frozen(false) {} -Status FailPointRegistry::addFailPoint(const std::string& name, FailPoint* failPoint) { +Status FailPointRegistry::add(const std::string& name, FailPoint* failPoint) { if (_frozen) { return {ErrorCodes::CannotMutateObject, "Registry is already frozen"}; } - - if (_fpMap.count(name) > 0) { - return {ErrorCodes::Error(51006), - str::stream() << "Fail point already registered: " << name}; + auto [pos, ok] = _fpMap.insert({name, failPoint}); + if (!ok) { + return {ErrorCodes::Error(51006), "Fail point already registered: {}"_format(name)}; } - - _fpMap.insert(make_pair(name, failPoint)); return Status::OK(); } -FailPoint* FailPointRegistry::getFailPoint(const std::string& name) const { - return mapFindWithDefault(_fpMap, name, static_cast<FailPoint*>(nullptr)); +FailPoint* FailPointRegistry::find(const std::string& name) const { + auto iter = _fpMap.find(name); + return (iter == _fpMap.end()) ? nullptr : iter->second; } void FailPointRegistry::freeze() { @@ -64,17 +62,19 @@ void FailPointRegistry::freeze() { } void FailPointRegistry::registerAllFailPointsAsServerParameters() { - for (const auto& it : _fpMap) { + for (const auto& [name, ptr] : _fpMap) { // Intentionally leaked. - new FailPointServerParameter(it.first, ServerParameterType::kStartupOnly); + new FailPointServerParameter(name, ServerParameterType::kStartupOnly); } } +static constexpr auto kFailPointServerParameterPrefix = "failpoint."_sd; + FailPointServerParameter::FailPointServerParameter(StringData name, ServerParameterType spt) - : ServerParameter(kFailPointServerParameterPrefix.toString() + name.toString(), spt), - _data(getGlobalFailPointRegistry()->getFailPoint(name.toString())) { + : ServerParameter("{}{}"_format(kFailPointServerParameterPrefix, name), spt), + _data(globalFailPointRegistry().find(name.toString())) { invariant(name != "failpoint.*", "Failpoint prototype was auto-registered from IDL"); - invariant(_data != nullptr, str::stream() << "Unknown failpoint: " << name); + invariant(_data != nullptr, "Unknown failpoint: {}"_format(name)); } void FailPointServerParameter::append(OperationContext* opCtx, @@ -95,14 +95,7 @@ Status FailPointServerParameter::setFromString(const std::string& str) { if (!swParsedOptions.isOK()) { return swParsedOptions.getStatus(); } - - FailPoint::Mode mode; - FailPoint::ValType val; - BSONObj data; - std::tie(mode, val, data) = std::move(swParsedOptions.getValue()); - - _data->setMode(mode, val, data); - + _data->setMode(std::move(swParsedOptions.getValue())); return Status::OK(); } } // namespace mongo diff --git a/src/mongo/util/fail_point_registry.h b/src/mongo/util/fail_point_registry.h index 7446db95eb1..7a77b47faba 100644 --- a/src/mongo/util/fail_point_registry.h +++ b/src/mongo/util/fail_point_registry.h @@ -53,12 +53,12 @@ public: * 51006 - if the given name already exists in this registry. * CannotMutateObject - if this registry is already frozen. */ - Status addFailPoint(const std::string& name, FailPoint* failPoint); + Status add(const std::string& name, FailPoint* failPoint); /** - * @return the fail point object registered. Returns NULL if it was not registered. + * @return the fail point object registered, or nullptr if it was not registered. */ - FailPoint* getFailPoint(const std::string& name) const; + FailPoint* find(const std::string& name) const; /** * Freezes this registry from being modified. diff --git a/src/mongo/util/fail_point_service.cpp b/src/mongo/util/fail_point_service.cpp index 77068f82100..44d8c69ae65 100644 --- a/src/mongo/util/fail_point_service.cpp +++ b/src/mongo/util/fail_point_service.cpp @@ -38,49 +38,34 @@ namespace mongo { -using std::unique_ptr; - MONGO_FAIL_POINT_DEFINE(dummy); // used by tests in jstests/fail_point -unique_ptr<FailPointRegistry> _fpRegistry(nullptr); - -MONGO_INITIALIZER_GENERAL(FailPointRegistry, - MONGO_NO_PREREQUISITES, - ("BeginGeneralStartupOptionRegistration")) -(InitializerContext* context) { - _fpRegistry.reset(new FailPointRegistry()); - return Status::OK(); -} +MONGO_INITIALIZER_GROUP(FailPointRegistry, (), ("BeginStartupOptionHandling")); -MONGO_INITIALIZER_GENERAL(AllFailPointsRegistered, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS) +MONGO_INITIALIZER_GENERAL(AllFailPointsRegistered, (), ()) (InitializerContext* context) { - _fpRegistry->freeze(); + globalFailPointRegistry().freeze(); return Status::OK(); } -FailPointRegistry* getGlobalFailPointRegistry() { - return _fpRegistry.get(); +FailPointRegistry& globalFailPointRegistry() { + static auto& p = *new FailPointRegistry(); + return p; } void setGlobalFailPoint(const std::string& failPointName, const BSONObj& cmdObj) { - FailPointRegistry* registry = getGlobalFailPointRegistry(); - FailPoint* failPoint = registry->getFailPoint(failPointName); + FailPoint* failPoint = globalFailPointRegistry().find(failPointName); if (failPoint == nullptr) uasserted(ErrorCodes::FailPointSetFailed, failPointName + " not found"); - FailPoint::Mode mode; - FailPoint::ValType val; - BSONObj data; - std::tie(mode, val, data) = uassertStatusOK(FailPoint::parseBSON(cmdObj)); - - failPoint->setMode(mode, val, data); + failPoint->setMode(uassertStatusOK(FailPoint::parseBSON(cmdObj))); warning() << "failpoint: " << failPointName << " set to: " << failPoint->toBSON(); } FailPointEnableBlock::FailPointEnableBlock(const std::string& failPointName) : _failPointName(failPointName) { - _failPoint = getGlobalFailPointRegistry()->getFailPoint(failPointName); + _failPoint = globalFailPointRegistry().find(failPointName); invariant(_failPoint != nullptr); _failPoint->setMode(FailPoint::alwaysOn); warning() << "failpoint: " << failPointName << " set to: " << _failPoint->toBSON(); @@ -88,7 +73,7 @@ FailPointEnableBlock::FailPointEnableBlock(const std::string& failPointName) FailPointEnableBlock::FailPointEnableBlock(const std::string& failPointName, const BSONObj& data) : _failPointName(failPointName) { - _failPoint = getGlobalFailPointRegistry()->getFailPoint(failPointName); + _failPoint = globalFailPointRegistry().find(failPointName); invariant(_failPoint != nullptr); _failPoint->setMode(FailPoint::alwaysOn, 0, data); warning() << "failpoint: " << failPointName << " set to: " << _failPoint->toBSON(); diff --git a/src/mongo/util/fail_point_service.h b/src/mongo/util/fail_point_service.h index 64c511942fc..8f7f3261b13 100644 --- a/src/mongo/util/fail_point_service.h +++ b/src/mongo/util/fail_point_service.h @@ -30,15 +30,13 @@ #pragma once #include "mongo/base/init.h" +#include "mongo/util/assert_util.h" #include "mongo/util/fail_point.h" #include "mongo/util/fail_point_registry.h" namespace mongo { -/** - * @return the global fail point registry. - */ -FailPointRegistry* getGlobalFailPointRegistry(); +FailPointRegistry& globalFailPointRegistry(); /** * Set a fail point in the global registry to a given value via BSON @@ -46,29 +44,27 @@ FailPointRegistry* getGlobalFailPointRegistry(); */ void setGlobalFailPoint(const std::string& failPointName, const BSONObj& cmdObj); +struct FailPointRegisterer { + FailPointRegisterer(const std::string& name, FailPoint* fp) { + uassertStatusOK(globalFailPointRegistry().add(name, fp)); + } +}; + /** * Convenience macro for defining a fail point. Must be used at namespace scope. * Note: that means never at local scope (inside functions) or class scope. * NOTE: Never use in header files, only sources. */ -#define MONGO_FAIL_POINT_DEFINE(fp) \ - ::mongo::FailPoint fp; \ - MONGO_INITIALIZER_GENERAL(fp, ("FailPointRegistry"), ("AllFailPointsRegistered")) \ - (::mongo::InitializerContext * context) { \ - return ::mongo::getGlobalFailPointRegistry()->addFailPoint(#fp, &fp); \ - } - -/** - * Convenience macro for declaring a fail point in a header. - */ -#define MONGO_FAIL_POINT_DECLARE(fp) extern ::mongo::FailPoint fp; +#define MONGO_FAIL_POINT_DEFINE(fp) \ + ::mongo::FailPoint fp; \ + ::mongo::FailPointRegisterer fp##failPointRegisterer(#fp, &fp); /** * Convenience class for enabling a failpoint and disabling it as this goes out of scope. */ class FailPointEnableBlock { public: - FailPointEnableBlock(const std::string& failPointName); + explicit FailPointEnableBlock(const std::string& failPointName); FailPointEnableBlock(const std::string& failPointName, const BSONObj& cmdObj); ~FailPointEnableBlock(); diff --git a/src/mongo/util/fail_point_test.cpp b/src/mongo/util/fail_point_test.cpp index 0a32ec45777..1880b0d18a1 100644 --- a/src/mongo/util/fail_point_test.cpp +++ b/src/mongo/util/fail_point_test.cpp @@ -46,7 +46,6 @@ using mongo::BSONObj; using mongo::FailPoint; using mongo::FailPointEnableBlock; -using mongo::getGlobalFailPointRegistry; namespace stdx = mongo::stdx; namespace mongo_test { @@ -60,7 +59,7 @@ TEST(FailPoint, AlwaysOn) { failPoint.setMode(FailPoint::alwaysOn); ASSERT(failPoint.shouldFail()); - MONGO_FAIL_POINT_BLOCK(failPoint, scopedFp) { + if (auto scopedFp = failPoint.scoped(); MONGO_unlikely(scopedFp.isActive())) { ASSERT(scopedFp.getData().isEmpty()); } @@ -85,11 +84,7 @@ TEST(FailPoint, NTimes) { TEST(FailPoint, BlockOff) { FailPoint failPoint; bool called = false; - - MONGO_FAIL_POINT_BLOCK(failPoint, scopedFp) { - called = true; - } - + failPoint.execute([&](const BSONObj&) { called = true; }); ASSERT_FALSE(called); } @@ -98,9 +93,7 @@ TEST(FailPoint, BlockAlwaysOn) { failPoint.setMode(FailPoint::alwaysOn); bool called = false; - MONGO_FAIL_POINT_BLOCK(failPoint, scopedFp) { - called = true; - } + failPoint.execute([&](const BSONObj&) { called = true; }); ASSERT(called); } @@ -111,9 +104,7 @@ TEST(FailPoint, BlockNTimes) { size_t counter = 0; for (size_t x = 0; x < 10; x++) { - MONGO_FAIL_POINT_BLOCK(failPoint, scopedFp) { - counter++; - } + failPoint.execute([&](auto&&...) { counter++; }); } ASSERT_EQUALS(1U, counter); @@ -125,9 +116,8 @@ TEST(FailPoint, BlockWithException) { bool threw = false; try { - MONGO_FAIL_POINT_BLOCK(failPoint, scopedFp) { - throw std::logic_error("BlockWithException threw"); - } + failPoint.execute( + [&](const BSONObj&) { throw std::logic_error("BlockWithException threw"); }); } catch (const std::logic_error&) { threw = true; } @@ -142,9 +132,7 @@ TEST(FailPoint, SetGetParam) { FailPoint failPoint; failPoint.setMode(FailPoint::alwaysOn, 0, BSON("x" << 20)); - MONGO_FAIL_POINT_BLOCK(failPoint, scopedFp) { - ASSERT_EQUALS(20, scopedFp.getData()["x"].numberInt()); - } + failPoint.execute([&](const BSONObj& data) { ASSERT_EQUALS(20, data["x"].numberInt()); }); } class FailPointStress : public mongo::unittest::Test { @@ -181,9 +169,7 @@ public: private: void blockTask() { while (true) { - MONGO_FAIL_POINT_BLOCK(_fp, scopedFp) { - const mongo::BSONObj& data = scopedFp.getData(); - + _fp.execute([](const BSONObj& data) { // Expanded ASSERT_EQUALS since the error is not being // printed out properly if (data["a"].numberInt() != 44) { @@ -191,7 +177,7 @@ private: << " - data: " << data << std::endl; ASSERT(false); } - } + }); stdx::lock_guard<stdx::mutex> lk(_mutex); if (_inShutdown) @@ -202,9 +188,7 @@ private: void blockWithExceptionTask() { while (true) { try { - MONGO_FAIL_POINT_BLOCK(_fp, scopedFp) { - const mongo::BSONObj& data = scopedFp.getData(); - + _fp.execute([](const BSONObj& data) { if (data["a"].numberInt() != 44) { mongo::error() << "blockWithExceptionTask thread detected anomaly" << " - data: " << data << std::endl; @@ -212,7 +196,7 @@ private: } throw std::logic_error("blockWithExceptionTask threw"); - } + }); } catch (const std::logic_error&) { } @@ -224,7 +208,7 @@ private: void simpleTask() { while (true) { - static_cast<void>(MONGO_FAIL_POINT(_fp)); + static_cast<void>(MONGO_unlikely(_fp.shouldFail())); stdx::lock_guard<stdx::mutex> lk(_mutex); if (_inShutdown) break; @@ -403,7 +387,7 @@ TEST(FailPoint, parseBSONValidDataSucceeds) { } TEST(FailPoint, FailPointBlockBasicTest) { - auto failPoint = getGlobalFailPointRegistry()->getFailPoint("dummy"); + auto failPoint = mongo::globalFailPointRegistry().find("dummy"); ASSERT_FALSE(failPoint->shouldFail()); @@ -418,33 +402,25 @@ TEST(FailPoint, FailPointBlockBasicTest) { TEST(FailPoint, FailPointBlockIfBasicTest) { FailPoint failPoint; failPoint.setMode(FailPoint::nTimes, 1, BSON("skip" << true)); - { bool hit = false; - - MONGO_FAIL_POINT_BLOCK_IF(failPoint, scopedFp, [&](const BSONObj& obj) { - hit = obj["skip"].trueValue(); - return false; - }) { - ASSERT(!"shouldn't get here"); - } - + failPoint.executeIf([](const BSONObj&) { ASSERT(!"shouldn't get here"); }, + [&hit](const BSONObj& obj) { + hit = obj["skip"].trueValue(); + return false; + }); ASSERT(hit); } - { bool hit = false; - - MONGO_FAIL_POINT_BLOCK_IF(failPoint, scopedFp, [](auto) { return true; }) { - hit = true; - ASSERT(!scopedFp.getData().isEmpty()); - } - + failPoint.executeIf( + [&hit](const BSONObj& data) { + hit = true; + ASSERT(!data.isEmpty()); + }, + [](const BSONObj&) { return true; }); ASSERT(hit); } - - MONGO_FAIL_POINT_BLOCK_IF(failPoint, scopedFp, [](auto) { return true; }) { - ASSERT(!"shouldn't get here"); - } + failPoint.executeIf([](auto&&) { ASSERT(!"shouldn't get here"); }, [](auto&&) { return true; }); } } // namespace mongo_test diff --git a/src/mongo/util/net/sock.cpp b/src/mongo/util/net/sock.cpp index b7cecfb3324..bd183130d5a 100644 --- a/src/mongo/util/net/sock.cpp +++ b/src/mongo/util/net/sock.cpp @@ -395,7 +395,7 @@ int Socket::_send(const char* data, int len, const char* context) { void Socket::send(const char* data, int len, const char* context) { while (len > 0) { int ret = -1; - if (MONGO_FAIL_POINT(throwSockExcep)) { + if (MONGO_unlikely(throwSockExcep.shouldFail())) { #if defined(_WIN32) WSASetLastError(WSAENETUNREACH); #else @@ -458,7 +458,7 @@ void Socket::send(const vector<pair<char*, int>>& data, const char* context) { while (meta.msg_iovlen > 0) { int ret = -1; - if (MONGO_FAIL_POINT(throwSockExcep)) { + if (MONGO_unlikely(throwSockExcep.shouldFail())) { #if defined(_WIN32) WSASetLastError(WSAENETUNREACH); #else @@ -491,7 +491,7 @@ void Socket::send(const vector<pair<char*, int>>& data, const char* context) { void Socket::recv(char* buf, int len) { while (len > 0) { int ret = -1; - if (MONGO_FAIL_POINT(throwSockExcep)) { + if (MONGO_unlikely(throwSockExcep.shouldFail())) { #if defined(_WIN32) WSASetLastError(WSAENETUNREACH); #else diff --git a/src/mongo/util/net/sock_test.cpp b/src/mongo/util/net/sock_test.cpp index d9b3ec8ee84..0d6d4c69017 100644 --- a/src/mongo/util/net/sock_test.cpp +++ b/src/mongo/util/net/sock_test.cpp @@ -208,7 +208,7 @@ const char kSocketFailPointName[] = "throwSockExcep"; class SocketFailPointTest : public unittest::Test { public: SocketFailPointTest() - : _failPoint(getGlobalFailPointRegistry()->getFailPoint(kSocketFailPointName)), + : _failPoint(globalFailPointRegistry().find(kSocketFailPointName)), _sockets(socketPair(SOCK_STREAM)) { ASSERT_TRUE(_failPoint != nullptr); ASSERT_TRUE(_sockets.first); diff --git a/src/mongo/util/net/ssl/detail/io.hpp b/src/mongo/util/net/ssl/detail/io.hpp index d6e376b00f0..f6ff6edd1d8 100644 --- a/src/mongo/util/net/ssl/detail/io.hpp +++ b/src/mongo/util/net/ssl/detail/io.hpp @@ -29,7 +29,7 @@ namespace ssl { namespace detail { // Failpoint to force small reads of data to exercise the SChannel buffering code -MONGO_FAIL_POINT_DECLARE(smallTLSReads); +extern ::mongo::FailPoint smallTLSReads; template <typename Stream, typename Operation> std::size_t io(Stream& next_layer, stream_core& core, const Operation& op, asio::error_code& ec) { @@ -42,7 +42,7 @@ std::size_t io(Stream& next_layer, stream_core& core, const Operation& op, asio: // the underlying transport. if (core.input_.size() == 0) { // Read tiny amounts of TLS data to test the SChannel buffering code - if (MONGO_FAIL_POINT(smallTLSReads)) { + if (MONGO_unlikely(smallTLSReads.shouldFail())) { core.input_ = asio::buffer(core.input_buffer_, next_layer.read_some( |