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 
thread_func(boost::barrier * barrier)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 
main()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 }
79