/*
* 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 .
*
* 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.
*/
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kControl
#include "mongo/util/fail_point.h"
#include "mongo/platform/random.h"
#include "mongo/stdx/thread.h"
#include "mongo/util/concurrency/threadlocal.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/time_support.h"
namespace mongo {
namespace {
/**
* Type representing the per-thread PRNG used by fail-points. Required because TSP_* macros,
* below, only let you create one thread-specific object per type.
*/
class FailPointPRNG {
public:
FailPointPRNG() : _prng(std::unique_ptr(SecureRandom::create())->nextInt64()) {}
void resetSeed(int32_t seed) {
_prng = PseudoRandom(seed);
}
int32_t nextPositiveInt32() {
return _prng.nextInt32() & ~(1 << 31);
}
private:
PseudoRandom _prng;
};
} // namespace
TSP_DECLARE(FailPointPRNG, failPointPrng);
TSP_DEFINE(FailPointPRNG, failPointPrng);
namespace {
int32_t prngNextPositiveInt32() {
return failPointPrng.getMake()->nextPositiveInt32();
}
} // namespace
void FailPoint::setThreadPRNGSeed(int32_t seed) {
failPointPrng.getMake()->resetSeed(seed);
}
FailPoint::FailPoint() : _fpInfo(0), _mode(off), _timesOrPeriod(0) {}
void FailPoint::shouldFailCloseBlock() {
_fpInfo.subtractAndFetch(1);
}
void FailPoint::setMode(Mode mode, ValType val, const BSONObj& extra) {
/**
* Outline:
*
* 1. Deactivates fail point to enter write-only mode
* 2. Waits for all current readers of the fail point to finish
* 3. Sets the new mode.
*/
stdx::lock_guard scoped(_modMutex);
// Step 1
disableFailPoint();
// Step 2
while (_fpInfo.load() != 0) {
sleepmillis(50);
}
_mode = mode;
_timesOrPeriod.store(val);
_data = extra.copy();
if (_mode != off) {
enableFailPoint();
}
}
const BSONObj& FailPoint::getData() const {
return _data;
}
void FailPoint::enableFailPoint() {
// TODO: Better to replace with a bitwise OR, once available for AU32
ValType currentVal = _fpInfo.load();
ValType expectedCurrentVal;
ValType newVal;
do {
expectedCurrentVal = currentVal;
newVal = expectedCurrentVal | ACTIVE_BIT;
currentVal = _fpInfo.compareAndSwap(expectedCurrentVal, newVal);
} while (expectedCurrentVal != currentVal);
}
void FailPoint::disableFailPoint() {
// TODO: Better to replace with a bitwise AND, once available for AU32
ValType currentVal = _fpInfo.load();
ValType expectedCurrentVal;
ValType newVal;
do {
expectedCurrentVal = currentVal;
newVal = expectedCurrentVal & REF_COUNTER_MASK;
currentVal = _fpInfo.compareAndSwap(expectedCurrentVal, newVal);
} while (expectedCurrentVal != currentVal);
}
FailPoint::RetCode FailPoint::slowShouldFailOpenBlock() {
ValType localFpInfo = _fpInfo.addAndFetch(1);
if ((localFpInfo & ACTIVE_BIT) == 0) {
return slowOff;
}
switch (_mode) {
case alwaysOn:
return slowOn;
case random: {
const AtomicInt32::WordType maxActivationValue = _timesOrPeriod.load();
if (prngNextPositiveInt32() < maxActivationValue) {
return slowOn;
}
return slowOff;
}
case nTimes: {
AtomicInt32::WordType newVal = _timesOrPeriod.subtractAndFetch(1);
if (newVal <= 0) {
disableFailPoint();
}
return slowOn;
}
default:
error() << "FailPoint Mode not supported: " << static_cast(_mode);
fassertFailed(16444);
}
}
BSONObj FailPoint::toBSON() const {
BSONObjBuilder builder;
stdx::lock_guard scoped(_modMutex);
builder.append("mode", _mode);
builder.append("data", _data);
return builder.obj();
}
}