/* Copyright (c) 2008-2010, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// This file is a part of a test suite for ThreadSanitizer, a race detector.
// Author: Konstantin Serebryany.
//
// C++ tests for atomicity violations (aka high-level races).
// See also: http://code.google.com/p/data-race-test/wiki/HighLevelDataRaces
#include "test_utils.h"
#include <gtest/gtest.h>
#include <map>
namespace AtomicityTests_LockedVector { // {{{1
// The most popular form of atomicity violation.
// Every method of a class is locked, but not every method is atomic.
// So,
// if(v.size() > 0)
// v.pop_back()
// may fail, if another thread called v.pop_back() in between.
class LockedVector {
public:
size_t size() {
MutexLock l(&mu_);
return v_.size();
}
void push_back(int a) {
MutexLock l(&mu_);
v_.push_back(a);
}
void pop_back() {
MutexLock l(&mu_);
v_.pop_back();
}
private:
vector<int> v_;
Mutex mu_;
};
const int N = 100;
LockedVector v;
void Worker() {
for (int i = 0; i < N; i++) {
if (v.size() > 0)
v.pop_back();
v.push_back(i);
usleep(1);
}
}
// The test is disabled because it actually fails sometimes.
// Run it with --gtest_also_run_disabled_tests
TEST(AtomicityTests, DISABLED_LockedVector) {
MyThreadArray t(Worker, Worker);
t.Start();
t.Join();
}
} // namespace
namespace AtomicityTests_ReaderThenWriterLockTest { // {{{1
#ifndef _MSC_VER
// Atomicity violation with a map and a reader lock.
// The function CheckMapAndInsertIfNeeded first checks if an element
// with a given key exists. If not, it inserts such element.
// The problem here is that during the first part we hold a reader lock,
// then we release it and grap writer lock, but the code has (incorrect)
// assumption that the map has not been changed between ReaderUnlock and
// WriterLock.
typedef std::map<int, int> Map;
Map *m;
RWLock mu;
bool reported = false;
void CheckMapAndInsertIfNeeded(int key, int val) {
Map::iterator it;
{
ReaderLockScoped reader(&mu);
it = m->find(key);
if (it != m->end())
return;
}
// <<<<< Another thread may change the map here.
{
WriterLockScoped writer(&mu);
// CHECK(m->find(key) == m->end());
if (m->find(key) != m->end()) {
if (!reported) {
printf("Here comes the result of atomicity violation!\n");
reported = true;
}
return;
}
(*m)[key] = val;
}
}
void Worker() {
for (int i = 0; i < 1000; i++) {
CheckMapAndInsertIfNeeded(i, i);
usleep(0);
}
}
TEST(AtomicityTests, ReaderThenWriterLockTest) {
m = new Map();
MyThreadArray t(Worker, Worker, Worker);
t.Start();
t.Join();
delete m;
}
#endif // _MSC_VER
} // namespace
// End {{{1
// vim:shiftwidth=2:softtabstop=2:expandtab:foldmethod=marker