]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // Copyright (c) 2020 Andrey Semashev |
2 | // | |
3 | // Distributed under the Boost Software License, Version 1.0. | |
4 | // See accompanying file LICENSE_1_0.txt or copy at | |
5 | // http://www.boost.org/LICENSE_1_0.txt) | |
6 | ||
7 | // This is a fuzzing test for waiting and notifying operations. | |
8 | // The test creates a number of threads exceeding the number of hardware threads, each of which | |
9 | // blocks on the atomic object. The main thread then notifies one or all threads repeatedly, | |
10 | // while incrementing the atomic object. The test ends when the atomic counter reaches the predefined limit. | |
11 | // The goal of the test is to verify that (a) it doesn't crash and (b) all threads get unblocked in the end. | |
12 | ||
13 | #include <boost/memory_order.hpp> | |
14 | #include <boost/atomic/atomic.hpp> | |
15 | ||
16 | #include <iostream> | |
17 | #include <boost/config.hpp> | |
18 | #include <boost/bind/bind.hpp> | |
19 | #include <boost/chrono/chrono.hpp> | |
20 | #include <boost/thread/thread.hpp> | |
21 | #include <boost/thread/barrier.hpp> | |
22 | #include <boost/smart_ptr/scoped_array.hpp> | |
23 | ||
24 | namespace chrono = boost::chrono; | |
25 | ||
26 | boost::atomic< unsigned int > g_atomic(0u); | |
27 | ||
28 | BOOST_CONSTEXPR_OR_CONST unsigned int loop_count = 4096u; | |
29 | ||
30 | void thread_func(boost::barrier* barrier) | |
31 | { | |
32 | barrier->wait(); | |
33 | ||
34 | unsigned int old_count = 0u; | |
35 | while (true) | |
36 | { | |
37 | unsigned int new_count = g_atomic.wait(old_count, boost::memory_order_relaxed); | |
38 | if (new_count >= loop_count) | |
39 | break; | |
40 | ||
41 | old_count = new_count; | |
42 | } | |
43 | } | |
44 | ||
45 | int main() | |
46 | { | |
47 | const unsigned int thread_count = boost::thread::hardware_concurrency() + 4u; | |
48 | boost::barrier barrier(thread_count + 1u); | |
49 | boost::scoped_array< boost::thread > threads(new boost::thread[thread_count]); | |
50 | ||
51 | for (unsigned int i = 0u; i < thread_count; ++i) | |
52 | boost::thread(boost::bind(&thread_func, &barrier)).swap(threads[i]); | |
53 | ||
54 | barrier.wait(); | |
55 | ||
56 | // Let the threads block on the atomic counter | |
57 | boost::this_thread::sleep_for(chrono::milliseconds(100)); | |
58 | ||
59 | while (true) | |
60 | { | |
61 | for (unsigned int i = 0u; i < thread_count; ++i) | |
62 | { | |
63 | g_atomic.opaque_add(1u, boost::memory_order_relaxed); | |
64 | g_atomic.notify_one(); | |
65 | } | |
66 | ||
67 | unsigned int old_count = g_atomic.fetch_add(1u, boost::memory_order_relaxed); | |
68 | g_atomic.notify_all(); | |
69 | ||
70 | if ((old_count + 1u) >= loop_count) | |
71 | break; | |
72 | } | |
73 | ||
74 | for (unsigned int i = 0u; i < thread_count; ++i) | |
75 | threads[i].join(); | |
76 | ||
77 | return 0u; | |
78 | } |