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
38namespace 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.
45class 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
67const int N = 100;
68LockedVector v;
69
70void 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
81TEST(AtomicityTests, DISABLED_LockedVector) {
82  MyThreadArray t(Worker, Worker);
83  t.Start();
84  t.Join();
85}
86
87}  // namespace
88
89
90namespace 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
100typedef std::map<int, int> Map;
101Map *m;
102RWLock mu;
103bool reported = false;
104
105void 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
129void Worker() {
130  for (int i = 0; i < 1000; i++) {
131    CheckMapAndInsertIfNeeded(i, i);
132    usleep(0);
133  }
134}
135
136TEST(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