/* 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