1// Copyright 2023 Peter Dimov.
2// Copyright 2023 Christian Mazakas.
3// Copyright 2023 Ed Catmur
4// Distributed under the Boost Software License, Version 1.0.
5// https://www.boost.org/LICENSE_1_0.txt
6
7#ifdef _MSC_VER
8#pragma warning(disable: 4530) // C++ exception handler used, but unwing semantics not enabled
9#pragma warning(disable: 4577) // noexcept used with no exception handling mode specified
10#endif
11
12#include <boost/compat/shared_lock.hpp>
13
14#include <boost/core/lightweight_test.hpp>
15#include <boost/core/lightweight_test_trait.hpp>
16#include <boost/throw_exception.hpp>
17#include <boost/config.hpp>
18
19#include <atomic>
20#include <memory>
21#include <mutex>
22#include <thread>
23#include <type_traits>
24#include <stdexcept>
25#include <cstdio>
26
27#define STATIC_ASSERT( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ )
28
29#ifdef __clang__
30#pragma clang diagnostic ignored "-Wself-move"
31#endif
32#if defined(__GNUC__) && __GNUC__ >= 13
33#pragma GCC diagnostic ignored "-Wself-move"
34#endif
35
36struct invalid_lock_use: public std::runtime_error
37{
38 invalid_lock_use(): std::runtime_error( "Invalid lock use" )
39 {
40 }
41};
42
43struct dummy_lock {
44private:
45 int lock_shared_count_ = 0;
46 int unlock_shared_count_ = 0;
47
48 int lock_unique_count_ = 0;
49 int unlock_unique_count_ = 0;
50
51public:
52 dummy_lock() = default;
53 ~dummy_lock() {
54 BOOST_TEST_EQ( lock_shared_count_, unlock_shared_count_ );
55 BOOST_TEST_EQ( lock_unique_count_, unlock_unique_count_ );
56 }
57
58 void lock() {
59 if ( lock_shared_count_ != unlock_shared_count_ ) {
60 boost::throw_exception( e: invalid_lock_use(), BOOST_CURRENT_LOCATION );
61 }
62 ++lock_unique_count_;
63 }
64
65 void unlock() { ++unlock_unique_count_; }
66
67 bool try_lock() {
68 if ( lock_shared_count_ != unlock_shared_count_ ) {
69 return false;
70 }
71
72 ++lock_unique_count_;
73 return true;
74 }
75
76 void lock_shared() {
77 if ( lock_unique_count_ != unlock_unique_count_ ) {
78 boost::throw_exception( e: invalid_lock_use(), BOOST_CURRENT_LOCATION );
79 }
80 ++lock_shared_count_;
81 }
82
83 bool try_lock_shared() {
84 if ( lock_unique_count_ != unlock_unique_count_ ) {
85 return false;
86 }
87 ++lock_shared_count_;
88 return true;
89 }
90
91 void unlock_shared() { ++unlock_shared_count_; }
92
93 int shared_lock_count() const noexcept { return lock_shared_count_; }
94};
95
96namespace {
97
98using shared_lock_type = boost::compat::shared_lock<dummy_lock>;
99
100// verify that our dummy_lock throws when we hope it does
101void sanity_tests() {
102 dummy_lock sp;
103 sp.lock();
104 BOOST_TEST_THROWS( sp.lock_shared(), invalid_lock_use );
105 sp.unlock();
106
107 sp.lock_shared();
108 BOOST_TEST_THROWS( sp.lock(), invalid_lock_use );
109 sp.unlock_shared();
110}
111
112void default_constructor() {
113 // shared_lock() noexcept;
114
115 BOOST_TEST_TRAIT_SAME( typename shared_lock_type::mutex_type, dummy_lock );
116 BOOST_TEST_TRAIT_TRUE(
117 (std::is_nothrow_default_constructible<shared_lock_type>));
118
119 shared_lock_type lock;
120 BOOST_TEST_EQ( lock.mutex(), nullptr );
121 BOOST_TEST( !lock.owns_lock() );
122 BOOST_TEST( !lock );
123}
124
125void locking_construtor() {
126 // explicit shared_lock( mutex_type& m );
127
128 dummy_lock sp;
129
130 {
131 shared_lock_type lock( sp );
132 BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
133 BOOST_TEST( lock.owns_lock() );
134 BOOST_TEST( lock );
135 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
136 }
137}
138
139void deferred_constructor() {
140 // shared_lock( mutex_type& m, defer_lock_t t ) noexcept;
141
142 dummy_lock sp;
143
144 shared_lock_type lock( sp, std::defer_lock );
145
146 BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
147 BOOST_TEST( !lock.owns_lock() );
148 BOOST_TEST( !lock );
149 BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
150}
151
152void try_lock_constructor() {
153 // shared_lock( mutex_type& m, try_to_lock_t t );
154
155 dummy_lock sp;
156
157 {
158 shared_lock_type lock( sp, std::try_to_lock );
159 BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
160 BOOST_TEST( lock.owns_lock() );
161 BOOST_TEST( lock );
162 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
163 }
164
165 {
166 sp.lock();
167
168 shared_lock_type lock( sp, std::try_to_lock );
169 BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
170 BOOST_TEST( !lock.owns_lock() );
171 BOOST_TEST( !lock );
172 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
173
174 sp.unlock();
175 }
176}
177
178void adopt_lock_constructor() {
179 // shared_lock(mutex_type& m, adopt_lock_t);
180
181 dummy_lock sp;
182 sp.lock_shared();
183
184 {
185 shared_lock_type lock( sp, std::adopt_lock );
186 BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
187 BOOST_TEST( lock.owns_lock() );
188 BOOST_TEST( lock );
189 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
190 }
191
192 sp.lock();
193 sp.unlock();
194}
195
196void move_constructor() {
197 BOOST_TEST_TRAIT_TRUE(
198 (std::is_nothrow_move_constructible<shared_lock_type>));
199
200 {
201 dummy_lock sp;
202
203 shared_lock_type lock( sp );
204 shared_lock_type lock2( std::move( lock ) );
205 BOOST_TEST_EQ( lock2.mutex(), std::addressof( sp ) );
206 BOOST_TEST( lock2.owns_lock() );
207 BOOST_TEST( lock2 );
208
209 BOOST_TEST_EQ( lock.mutex(), nullptr );
210 BOOST_TEST( !lock.owns_lock() );
211 BOOST_TEST( !lock );
212
213 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
214 }
215
216 {
217 dummy_lock sp;
218
219 shared_lock_type lock( sp, std::defer_lock );
220 shared_lock_type lock2( std::move( lock ) );
221 BOOST_TEST_EQ( lock2.mutex(), std::addressof( sp ) );
222 BOOST_TEST( !lock2.owns_lock() );
223 BOOST_TEST( !lock2 );
224
225 BOOST_TEST_EQ( lock.mutex(), nullptr );
226 BOOST_TEST( !lock.owns_lock() );
227 BOOST_TEST( !lock );
228
229 BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
230 }
231
232 {
233 dummy_lock sp;
234
235 shared_lock_type lock;
236 shared_lock_type lock2( std::move( lock ) );
237 BOOST_TEST_EQ( lock2.mutex(), nullptr );
238 BOOST_TEST( !lock2.owns_lock() );
239 BOOST_TEST( !lock2 );
240
241 BOOST_TEST_EQ( lock.mutex(), nullptr );
242 BOOST_TEST( !lock.owns_lock() );
243 BOOST_TEST( !lock );
244
245 BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
246 }
247}
248
249void move_assignment() {
250 BOOST_TEST_TRAIT_TRUE( (std::is_nothrow_move_assignable<shared_lock_type>));
251
252 {
253 // lhs not bound, not locked
254 // rhs not bound, not locked
255 dummy_lock sp, sp2;
256
257 shared_lock_type lock;
258 shared_lock_type lock2;
259
260 lock2 = std::move( lock );
261 BOOST_TEST_EQ( lock2.mutex(), nullptr );
262 BOOST_TEST( !lock2.owns_lock() );
263 BOOST_TEST( !lock2 );
264
265 BOOST_TEST_EQ( lock.mutex(), nullptr );
266 BOOST_TEST( !lock.owns_lock() );
267 BOOST_TEST( !lock );
268
269 BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
270 BOOST_TEST_EQ( sp2.shared_lock_count(), 0 );
271 }
272
273 {
274 // lhs not bound, not locked
275 // rhs bound, locked
276 dummy_lock sp;
277
278 shared_lock_type lock( sp );
279 shared_lock_type lock2;
280
281 lock2 = std::move( lock );
282 BOOST_TEST_EQ( lock2.mutex(), std::addressof( sp ) );
283 BOOST_TEST( lock2.owns_lock() );
284 BOOST_TEST( lock2 );
285
286 BOOST_TEST_EQ( lock.mutex(), nullptr );
287 BOOST_TEST( !lock.owns_lock() );
288 BOOST_TEST( !lock );
289
290 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
291 }
292
293 {
294 // lhs not bound, not locked
295 // rhs bound, not locked
296 dummy_lock sp, sp2;
297
298 shared_lock_type lock( sp, std::defer_lock );
299 shared_lock_type lock2;
300
301 lock2 = std::move( lock );
302 BOOST_TEST_EQ( lock2.mutex(), std::addressof( sp ) );
303 BOOST_TEST( !lock2.owns_lock() );
304 BOOST_TEST( !lock2 );
305
306 BOOST_TEST_EQ( lock.mutex(), nullptr );
307 BOOST_TEST( !lock.owns_lock() );
308 BOOST_TEST( !lock );
309
310 BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
311 BOOST_TEST_EQ( sp2.shared_lock_count(), 0 );
312 }
313
314 {
315 // lhs bound, locked
316 // rhs not bound, not locked
317
318 dummy_lock sp, sp2;
319
320 shared_lock_type lock;
321 shared_lock_type lock2( sp2 );
322
323 lock2 = std::move( lock );
324
325 BOOST_TEST_EQ( lock2.mutex(), nullptr );
326 BOOST_TEST( !lock2.owns_lock() );
327 BOOST_TEST( !lock2 );
328
329 BOOST_TEST_EQ( lock.mutex(), nullptr );
330 BOOST_TEST( !lock.owns_lock() );
331 BOOST_TEST( !lock );
332
333 BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
334 BOOST_TEST_EQ( sp2.shared_lock_count(), 1 );
335 }
336
337 {
338 // lhs bound, not locked
339 // rhs not bound, not locked
340
341 dummy_lock sp, sp2;
342
343 shared_lock_type lock;
344 shared_lock_type lock2( sp, std::defer_lock );
345
346 lock2 = std::move( lock );
347
348 BOOST_TEST_EQ( lock2.mutex(), nullptr );
349 BOOST_TEST( !lock2.owns_lock() );
350 BOOST_TEST( !lock2 );
351
352 BOOST_TEST_EQ( lock.mutex(), nullptr );
353 BOOST_TEST( !lock.owns_lock() );
354
355 BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
356 BOOST_TEST_EQ( sp2.shared_lock_count(), 0 );
357 }
358
359 {
360 // lhs bound, locked
361 // rhs bound, locked
362
363 dummy_lock sp, sp2;
364
365 shared_lock_type lock( sp );
366 shared_lock_type lock2( sp2 );
367
368 lock2 = std::move( lock );
369 BOOST_TEST_EQ( lock2.mutex(), std::addressof( sp ) );
370 BOOST_TEST( lock2.owns_lock() );
371 BOOST_TEST( lock2 );
372
373 BOOST_TEST_EQ( lock.mutex(), nullptr );
374 BOOST_TEST( !lock.owns_lock() );
375 BOOST_TEST( !lock );
376
377 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
378 BOOST_TEST_EQ( sp2.shared_lock_count(), 1 );
379 }
380
381 {
382 // self-assign, locked
383
384 dummy_lock sp;
385 shared_lock_type lock( sp );
386
387 lock = std::move( lock );
388 BOOST_TEST_EQ( lock.mutex(), std::addressof( sp ) );
389 BOOST_TEST( lock.owns_lock() );
390 BOOST_TEST( lock );
391
392 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
393 }
394}
395
396void lock() {
397 {
398 dummy_lock sp;
399 shared_lock_type lock( sp, std::defer_lock );
400
401 lock.lock();
402 BOOST_TEST( lock.owns_lock() );
403 BOOST_TEST( lock );
404 BOOST_TEST_THROWS( lock.lock(), std::system_error );
405
406 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
407 }
408
409 {
410 dummy_lock sp;
411 shared_lock_type lock;
412 BOOST_TEST_THROWS( lock.lock(), std::system_error );
413 BOOST_TEST_EQ( sp.shared_lock_count(), 0 );
414 }
415}
416
417void unlock() {
418 dummy_lock sp;
419
420 shared_lock_type lock( sp );
421 lock.unlock();
422
423 BOOST_TEST( !lock.owns_lock() );
424 BOOST_TEST( !lock );
425
426 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
427
428 sp.lock();
429 sp.unlock();
430
431 BOOST_TEST_THROWS( lock.unlock(), std::system_error );
432}
433
434void try_lock() {
435 dummy_lock sp;
436
437 {
438 shared_lock_type lock( sp, std::defer_lock );
439 BOOST_TEST( lock.try_lock() );
440 BOOST_TEST( lock.owns_lock() );
441 BOOST_TEST( lock );
442
443 BOOST_TEST( !sp.try_lock() );
444
445 BOOST_TEST_THROWS( lock.try_lock(), std::system_error );
446
447 lock.unlock();
448
449 sp.lock();
450
451 BOOST_TEST( !lock.try_lock() );
452 BOOST_TEST( !lock.owns_lock() );
453 BOOST_TEST( !lock );
454
455 sp.unlock();
456 }
457
458 {
459 shared_lock_type lock;
460 BOOST_TEST_THROWS( lock.try_lock(), std::system_error );
461 }
462}
463
464void swap() {
465 dummy_lock sp, sp2;
466
467 {
468 shared_lock_type lock( sp );
469 shared_lock_type lock2( sp2 );
470
471 STATIC_ASSERT( noexcept( lock.swap( lock2 ) ) );
472 STATIC_ASSERT( noexcept( swap( lock, lock2 ) ) );
473
474 lock.swap( u&: lock2 );
475
476 BOOST_TEST_EQ( lock.mutex(), &sp2 );
477 BOOST_TEST( lock.owns_lock() );
478 BOOST_TEST( lock );
479
480 BOOST_TEST_EQ( lock2.mutex(), &sp );
481 BOOST_TEST( lock2.owns_lock() );
482 BOOST_TEST( lock2 );
483
484 swap( x&: lock, y&: lock2 );
485
486 BOOST_TEST_EQ( lock.mutex(), &sp );
487 BOOST_TEST( lock.owns_lock() );
488 BOOST_TEST( lock );
489
490 BOOST_TEST_EQ( lock2.mutex(), &sp2 );
491 BOOST_TEST( lock2.owns_lock() );
492 BOOST_TEST( lock2 );
493
494 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
495 BOOST_TEST_EQ( sp2.shared_lock_count(), 1 );
496 }
497
498 {
499 shared_lock_type lock( sp );
500 lock.swap( u&: lock );
501
502 BOOST_TEST_EQ( lock.mutex(), &sp );
503 BOOST_TEST( lock.owns_lock() );
504 BOOST_TEST( lock );
505 }
506}
507
508void release() {
509 dummy_lock sp;
510
511 shared_lock_type lock( sp );
512
513 dummy_lock* pm = lock.release();
514
515 BOOST_TEST_EQ( pm, &sp );
516 BOOST_TEST_EQ( lock.mutex(), nullptr );
517 BOOST_TEST( !lock.owns_lock() );
518 BOOST_TEST( !lock );
519
520 pm = lock.release();
521
522 BOOST_TEST_EQ( lock.mutex(), nullptr );
523 BOOST_TEST( !lock.owns_lock() );
524 BOOST_TEST( !lock );
525
526 BOOST_TEST_EQ( sp.shared_lock_count(), 1 );
527 sp.unlock_shared();
528}
529
530} // namespace
531
532int main() {
533 sanity_tests();
534
535 default_constructor();
536 locking_construtor();
537 deferred_constructor();
538 try_lock_constructor();
539 adopt_lock_constructor();
540
541 move_constructor();
542 move_assignment();
543
544 lock();
545 unlock();
546 try_lock();
547
548 swap();
549 release();
550
551 return boost::report_errors();
552}
553
554#ifdef BOOST_NO_EXCEPTIONS
555
556namespace boost
557{
558
559BOOST_NORETURN void throw_exception( std::exception const& ex, boost::source_location const& loc )
560{
561 std::fprintf( stderr, "Exception '%s' at %s:%d\n", ex.what(), loc.file_name(), loc.line() );
562 std::terminate();
563}
564
565} // namespace boost
566
567#endif
568

source code of boost/libs/compat/test/shared_lock_test.cpp