Home | History | Annotate | Download | only in unittest
      1 /* Copyright (c) 2008-2010, Google Inc.
      2  * All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Neither the name of Google Inc. nor the names of its
     11  * contributors may be used to endorse or promote products derived from
     12  * this software without specific prior written permission.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 // This file is a part of a test suite for ThreadSanitizer, a race detector.
     28 // Author: Konstantin Serebryany.
     29 //
     30 // C++ tests for atomicity violations (aka high-level races).
     31 // See also: http://code.google.com/p/data-race-test/wiki/HighLevelDataRaces
     32 #include "test_utils.h"
     33 
     34 #include <gtest/gtest.h>
     35 
     36 #include <map>
     37 
     38 namespace AtomicityTests_LockedVector {  // {{{1
     39 // The most popular form of atomicity violation.
     40 // Every method of a class is locked, but not every method is atomic.
     41 // So,
     42 //   if(v.size() > 0)
     43 //     v.pop_back()
     44 // may fail, if another thread called v.pop_back() in between.
     45 class LockedVector {
     46  public:
     47   size_t size() {
     48     MutexLock l(&mu_);
     49     return v_.size();
     50   }
     51 
     52   void push_back(int a) {
     53     MutexLock l(&mu_);
     54     v_.push_back(a);
     55   }
     56 
     57   void pop_back() {
     58     MutexLock l(&mu_);
     59     v_.pop_back();
     60   }
     61 
     62  private:
     63   vector<int> v_;
     64   Mutex       mu_;
     65 };
     66 
     67 const int N = 100;
     68 LockedVector v;
     69 
     70 void Worker() {
     71   for (int i = 0; i < N; i++) {
     72     if (v.size() > 0)
     73       v.pop_back();
     74     v.push_back(i);
     75     usleep(1);
     76   }
     77 }
     78 
     79 // The test is disabled because it actually fails sometimes.
     80 // Run it with --gtest_also_run_disabled_tests
     81 TEST(AtomicityTests, DISABLED_LockedVector) {
     82   MyThreadArray t(Worker, Worker);
     83   t.Start();
     84   t.Join();
     85 }
     86 
     87 }  // namespace
     88 
     89 
     90 namespace AtomicityTests_ReaderThenWriterLockTest {  // {{{1
     91 #ifndef _MSC_VER
     92 // Atomicity violation with a map and a reader lock.
     93 // The function CheckMapAndInsertIfNeeded first checks if an element
     94 // with a given key exists. If not, it inserts such element.
     95 // The problem here is that during the first part we hold a reader lock,
     96 // then we release it and grap writer lock, but the code has (incorrect)
     97 // assumption that the map has not been changed between ReaderUnlock and
     98 // WriterLock.
     99 
    100 typedef std::map<int, int> Map;
    101 Map *m;
    102 RWLock mu;
    103 bool reported = false;
    104 
    105 void CheckMapAndInsertIfNeeded(int key, int val) {
    106   Map::iterator it;
    107 
    108   {
    109     ReaderLockScoped reader(&mu);
    110     it = m->find(key);
    111     if (it != m->end())
    112       return;
    113   }
    114   // <<<<< Another thread may change the map here.
    115   {
    116     WriterLockScoped writer(&mu);
    117     // CHECK(m->find(key) == m->end());
    118     if (m->find(key) != m->end()) {
    119       if (!reported) {
    120         printf("Here comes the result of atomicity violation!\n");
    121         reported = true;
    122       }
    123       return;
    124     }
    125     (*m)[key] = val;
    126   }
    127 }
    128 
    129 void Worker() {
    130   for (int i = 0; i < 1000; i++) {
    131     CheckMapAndInsertIfNeeded(i, i);
    132     usleep(0);
    133   }
    134 }
    135 
    136 TEST(AtomicityTests, ReaderThenWriterLockTest) {
    137   m = new Map();
    138   MyThreadArray t(Worker, Worker, Worker);
    139   t.Start();
    140   t.Join();
    141   delete m;
    142 }
    143 #endif  // _MSC_VER
    144 }  // namespace
    145 
    146 // End {{{1
    147 // vim:shiftwidth=2:softtabstop=2:expandtab:foldmethod=marker
    148