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