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:
size()47 size_t size() {
48 MutexLock l(&mu_);
49 return v_.size();
50 }
51
push_back(int a)52 void push_back(int a) {
53 MutexLock l(&mu_);
54 v_.push_back(a);
55 }
56
pop_back()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
Worker()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
TEST(AtomicityTests,DISABLED_LockedVector)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
CheckMapAndInsertIfNeeded(int key,int val)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
Worker()129 void Worker() {
130 for (int i = 0; i < 1000; i++) {
131 CheckMapAndInsertIfNeeded(i, i);
132 usleep(0);
133 }
134 }
135
TEST(AtomicityTests,ReaderThenWriterLockTest)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