/*
* 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.
*/
#include "mongo/util/fail_point.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/time_support.h"
using mongoutils::str::stream;
namespace mongo {
FailPoint::FailPoint():
_fpInfo(0),
_mode(off),
_timesOrPeriod(0),
_modMutex("failPointMutex") {
}
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.
*/
scoped_lock scoped(_modMutex);
// Step 1
disableFailPoint();
// Step 2
while (_fpInfo.load() != 0) {
sleepmillis(50);
}
// Step 3
uassert(16442, stream() << "mode not supported " << static_cast(mode),
mode >= off && mode < numModes);
_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:
// TODO: randomly determine if should be active or not
error() << "FailPoint Mode random is not yet supported." << endl;
fassertFailed(16443);
case nTimes:
{
AtomicInt32::WordType newVal = _timesOrPeriod.subtractAndFetch(1);
if (newVal <= 0) {
disableFailPoint();
}
return slowOn;
}
default:
error() << "FailPoint Mode not supported: " << static_cast(_mode) << endl;
fassertFailed(16444);
}
}
BSONObj FailPoint::toBSON() const {
BSONObjBuilder builder;
scoped_lock scoped(_modMutex);
builder.append("mode", _mode);
builder.append("data", _data);
return builder.obj();
}
ScopedFailPoint::ScopedFailPoint(FailPoint* failPoint):
_failPoint(failPoint),
_once(false),
_shouldClose(false) {
}
ScopedFailPoint::~ScopedFailPoint() {
if (_shouldClose) {
_failPoint->shouldFailCloseBlock();
}
}
const BSONObj& ScopedFailPoint::getData() const {
// Assert when attempting to get data without incrementing ref counter.
fassert(16445, _shouldClose);
return _failPoint->getData();
}
}