1/*
2 Copyright (c) 2005-2019 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17// test reader_writer_lock
18#include "tbb/reader_writer_lock.h"
19#include "tbb/atomic.h"
20#include "tbb/tbb_exception.h"
21#include "harness.h"
22#include "harness_barrier.h"
23
24tbb::reader_writer_lock the_mutex;
25const int MAX_WORK = 10000;
26
27tbb::atomic<size_t> active_readers, active_writers;
28tbb::atomic<bool> sim_readers;
29size_t n_tested__sim_readers;
30
31
32int BusyWork(int percentOfMaxWork) {
33 int iters = 0;
34 for (int i=0; i<MAX_WORK*((double)percentOfMaxWork/100.0); ++i) {
35 iters++;
36 }
37 return iters;
38}
39
40struct StressRWLBody : NoAssign {
41 const int nThread;
42 const int percentMax;
43
44 StressRWLBody(int nThread_, int percentMax_) : nThread(nThread_), percentMax(percentMax_) {}
45
46 void operator()(const int /* threadID */ ) const {
47 int nIters = 100;
48 int r_result=0, w_result=0;
49 for(int i=0; i<nIters; ++i) {
50 // test unscoped blocking write lock
51 the_mutex.lock();
52 w_result += BusyWork(percentMax);
53#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
54 // test exception for recursive write lock
55 bool was_caught = false;
56 try {
57 the_mutex.lock();
58 }
59 catch(tbb::improper_lock& ex) {
60 REMARK("improper_lock: %s\n", ex.what());
61 was_caught = true;
62 }
63 catch(...) {
64 REPORT("Wrong exception caught during recursive lock attempt.");
65 }
66 ASSERT(was_caught, "Recursive lock attempt exception not caught properly.");
67 // test exception for recursive read lock
68 was_caught = false;
69 try {
70 the_mutex.lock_read();
71 }
72 catch(tbb::improper_lock& ex) {
73 REMARK("improper_lock: %s\n", ex.what());
74 was_caught = true;
75 }
76 catch(...) {
77 REPORT("Wrong exception caught during recursive lock attempt.");
78 }
79 ASSERT(was_caught, "Recursive lock attempt exception not caught properly.");
80#endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
81 the_mutex.unlock();
82 // test unscoped non-blocking write lock
83 if (the_mutex.try_lock()) {
84 w_result += BusyWork(percentMax);
85 the_mutex.unlock();
86 }
87 // test unscoped blocking read lock
88 the_mutex.lock_read();
89 r_result += BusyWork(percentMax);
90 the_mutex.unlock();
91 // test unscoped non-blocking read lock
92 if(the_mutex.try_lock_read()) {
93 r_result += BusyWork(percentMax);
94 the_mutex.unlock();
95 }
96 { // test scoped blocking write lock
97 tbb::reader_writer_lock::scoped_lock my_lock(the_mutex);
98 w_result += BusyWork(percentMax);
99 }
100 { // test scoped blocking read lock
101 tbb::reader_writer_lock::scoped_lock_read my_lock(the_mutex);
102 r_result += BusyWork(percentMax);
103 }
104 }
105 REMARK(" R%d/W%d", r_result, w_result); // reader/writer iterations of busy work completed
106 }
107};
108
109struct CorrectRWLScopedBody : NoAssign {
110 const int nThread;
111 Harness::SpinBarrier& my_barrier;
112
113 CorrectRWLScopedBody(int nThread_, Harness::SpinBarrier& b_) : nThread(nThread_),my_barrier(b_) {}
114
115 void operator()(const int /* threadID */ ) const {
116 my_barrier.wait();
117 for (int i=0; i<50; i++) {
118 const bool is_reader = i%5==0; // 1 writer for every 4 readers
119
120 if (is_reader) {
121 tbb::reader_writer_lock::scoped_lock_read my_lock(the_mutex);
122 active_readers++;
123 if (active_readers > 1) sim_readers = true;
124 ASSERT(active_writers==0, "Active writers in read-locked region.");
125 Harness::Sleep(10);
126 active_readers--;
127 }
128 else { // is writer
129 tbb::reader_writer_lock::scoped_lock my_lock(the_mutex);
130 active_writers++;
131 ASSERT(active_readers==0, "Active readers in write-locked region.");
132 ASSERT(active_writers<=1, "More than one active writer in write-locked region.");
133 Harness::Sleep(10);
134 active_writers--;
135 }
136 }
137 }
138};
139
140struct CorrectRWLBody : NoAssign {
141 const int nThread;
142 Harness::SpinBarrier& my_barrier;
143
144 CorrectRWLBody(int nThread_, Harness::SpinBarrier& b_ ) : nThread(nThread_), my_barrier(b_) {}
145
146 void operator()(const int /* threadID */ ) const {
147 my_barrier.wait();
148 for (int i=0; i<50; i++) {
149 const bool is_reader = i%5==0; // 1 writer for every 4 readers
150
151 if (is_reader) {
152 the_mutex.lock_read();
153 active_readers++;
154 if (active_readers > 1) sim_readers = true;
155 ASSERT(active_writers==0, "Active writers in read-locked region.");
156 }
157 else { // is writer
158 the_mutex.lock();
159 active_writers++;
160 ASSERT(active_readers==0, "Active readers in write-locked region.");
161 ASSERT(active_writers<=1, "More than one active writer in write-locked region.");
162 }
163 Harness::Sleep(10);
164 if (is_reader) {
165 active_readers--;
166 }
167 else { // is writer
168 active_writers--;
169 }
170 the_mutex.unlock();
171 }
172 }
173};
174
175void TestReaderWriterLockOnNThreads(int nThreads) {
176 // Stress-test all interfaces
177 for (int pc=0; pc<=100; pc+=20) {
178 REMARK("Testing with %d threads, percent of MAX_WORK=%d...", nThreads, pc);
179 StressRWLBody myStressBody(nThreads, pc);
180 NativeParallelFor(nThreads, myStressBody);
181 REMARK(" OK.\n");
182 }
183
184 int i;
185 n_tested__sim_readers = 0;
186 REMARK("Testing with %d threads, direct/unscoped locking mode...", nThreads); // TODO: choose direct or unscoped?
187 // TODO: refactor the following two for loops into a shared function
188 for( i=0; i<100; ++i ) {
189 Harness::SpinBarrier bar0(nThreads);
190
191 CorrectRWLBody myCorrectBody(nThreads,bar0);
192 active_writers = active_readers = 0;
193 sim_readers = false;
194 NativeParallelFor(nThreads, myCorrectBody);
195
196 if( sim_readers || nThreads==1 ) {
197 if( ++n_tested__sim_readers>5 )
198 break;
199 }
200 }
201 ASSERT(i<100, "There were no simultaneous readers.");
202 REMARK(" OK.\n");
203
204 n_tested__sim_readers = 0;
205 REMARK("Testing with %d threads, scoped locking mode...", nThreads);
206 for( i=0; i<100; ++i ) {
207 Harness::SpinBarrier bar0(nThreads);
208 CorrectRWLScopedBody myCorrectScopedBody(nThreads, bar0);
209 active_writers = active_readers = 0;
210 sim_readers = false;
211 NativeParallelFor(nThreads, myCorrectScopedBody);
212 if( sim_readers || nThreads==1 ) {
213 if( ++n_tested__sim_readers>5 )
214 break;
215 }
216 }
217 ASSERT(i<100, "There were no simultaneous readers.");
218 REMARK(" OK.\n");
219}
220
221void TestReaderWriterLock() {
222 for(int p = MinThread; p <= MaxThread; p++) {
223 TestReaderWriterLockOnNThreads(p);
224 }
225}
226
227
228int TestMain() {
229 if(MinThread <= 0) MinThread = 1;
230 if(MaxThread > 0) {
231 TestReaderWriterLock();
232 }
233 return Harness::Done;
234}
235