diff options
Diffstat (limited to 'src/mongo/util/fail_point.h')
-rw-r--r-- | src/mongo/util/fail_point.h | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/mongo/util/fail_point.h b/src/mongo/util/fail_point.h new file mode 100644 index 00000000000..7862db9e39e --- /dev/null +++ b/src/mongo/util/fail_point.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2012 10gen 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/>. + */ + +#pragma once + +#include "mongo/db/jsobj.h" +#include "mongo/platform/atomic_word.h" +#include "mongo/util/concurrency/mutex.h" + +namespace mongo { + /** + * A simple thread-safe fail point implementation that can be activated and + * 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. + * + * Sample use: + * // Declared somewhere: + * FailPoint makeBadThingsHappen; + * + * // Somewhere in the code + * return false || MONGO_FAIL_POINT(makeBadThingsHappen); + * + * or + * + * // Somewhere in the code + * MONGO_FAIL_POINT_BLOCK(makeBadThingsHappen) { + * const BSONObj& data = makeBadThingsHappen.getData(); + * // Do something + * } + * + * Invariants: + * + * 1. Always refer to _fpInfo first to check if failPoint is active or not before + * entering fail point or modifying fail point. + * 2. Client visible fail point states are read-only when active. + */ + class FailPoint { + public: + typedef AtomicUInt32::WordType ValType; + enum Mode { off, alwaysOn, random, nTimes, numModes }; + enum RetCode { fastOff = 0, slowOff, slowOn }; + + FailPoint(); + + /** + * Note: This is not side-effect free - it can change the state to OFF after calling. + * + * @return true if fail point is active. + */ + inline bool shouldFail() { + RetCode ret = shouldFailOpenBlock(); + + if (MONGO_likely(ret == fastOff)) { + return false; + } + + shouldFailCloseBlock(); + 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. + * + * @return slowOn if fail point is active. + */ + inline RetCode shouldFailOpenBlock() { + // TODO: optimization - use unordered load once available + if (MONGO_likely((_fpInfo.load() & ACTIVE_BIT) == 0)) { + return fastOff; + } + + return slowShouldFailOpenBlock(); + } + + /** + * 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. + * + * @param mode the new mode for this fail point. + * @param val the value that can have different usage depending on the mode: + * + * - off, alwaysOn: ignored + * - random: + * - nTimes: the number of times this fail point will be active when + * #shouldFail or #shouldFailOpenBlock is called. + * + * @param extra arbitrary BSON object that can be stored to this fail point + * that can be referenced afterwards with #getData. Defaults to an empty + * document. + */ + void setMode(Mode mode, ValType val = 0, const BSONObj& extra = BSONObj()); + + /** + * @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; + + 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. + AtomicUInt32 _fpInfo; + + // Invariant: These should be read only if ACTIVE_BIT of _fpInfo is set. + Mode _mode; + AtomicInt32 _timesOrPeriod; + BSONObj _data; + + // protects _mode, _timesOrPeriod, _data + mutex _modMutex; + + /** + * Disables this fail point. + */ + void disableFailPoint(); + + /** + * slow path for #shouldFailOpenBlock + */ + RetCode slowShouldFailOpenBlock(); + }; + + /** + * Helper class for making sure that FailPoint#shouldFailCloseBlock is called when + * FailPoint#shouldFailOpenBlock was called. + */ + class ScopedFailPoint { + public: + ScopedFailPoint(FailPoint* failPoint); + ~ScopedFailPoint(); + + /** + * @return true if fail point is on. This will be true at most once. + */ + inline bool isActive() { + if (_once) { + return false; + } + + _once = true; + + FailPoint::RetCode ret = _failPoint->shouldFailOpenBlock(); + _shouldClose = ret != FailPoint::fastOff; + return ret == FailPoint::slowOn; + } + + private: + FailPoint* _failPoint; + bool _once; + bool _shouldClose; + }; + + #define MONGO_FAIL_POINT(symbol) MONGO_unlikely(symbol.shouldFail()) + #define MONGO_FAIL_POINT_BLOCK(symbol) for (mongo::ScopedFailPoint scopedFP(&symbol); \ + MONGO_unlikely(scopedFP.isActive()); ) +} |