1*99451b44SJordan Rupprecht // This test is intended to create a situation in which one thread will exit
2*99451b44SJordan Rupprecht // while a breakpoint is being handled in another thread.  This may not always
3*99451b44SJordan Rupprecht // happen because it's possible that the exiting thread will exit before the
4*99451b44SJordan Rupprecht // breakpoint is hit.  The test case should be flexible enough to treat that
5*99451b44SJordan Rupprecht // as success.
6*99451b44SJordan Rupprecht 
7*99451b44SJordan Rupprecht #include "pseudo_barrier.h"
8*99451b44SJordan Rupprecht #include <chrono>
9*99451b44SJordan Rupprecht #include <thread>
10*99451b44SJordan Rupprecht 
11*99451b44SJordan Rupprecht volatile int g_test = 0;
12*99451b44SJordan Rupprecht 
13*99451b44SJordan Rupprecht // A barrier to synchronize all the threads.
14*99451b44SJordan Rupprecht pseudo_barrier_t g_barrier1;
15*99451b44SJordan Rupprecht 
16*99451b44SJordan Rupprecht // A barrier to keep the threads from exiting until after the breakpoint has
17*99451b44SJordan Rupprecht // been passed.
18*99451b44SJordan Rupprecht pseudo_barrier_t g_barrier2;
19*99451b44SJordan Rupprecht 
20*99451b44SJordan Rupprecht void *
break_thread_func()21*99451b44SJordan Rupprecht break_thread_func ()
22*99451b44SJordan Rupprecht {
23*99451b44SJordan Rupprecht     // Wait until all the threads are running
24*99451b44SJordan Rupprecht     pseudo_barrier_wait(g_barrier1);
25*99451b44SJordan Rupprecht 
26*99451b44SJordan Rupprecht     // Wait for the join thread to join
27*99451b44SJordan Rupprecht     std::this_thread::sleep_for(std::chrono::microseconds(50));
28*99451b44SJordan Rupprecht 
29*99451b44SJordan Rupprecht     // Do something
30*99451b44SJordan Rupprecht     g_test++;       // Set breakpoint here
31*99451b44SJordan Rupprecht 
32*99451b44SJordan Rupprecht     // Synchronize after the breakpoint
33*99451b44SJordan Rupprecht     pseudo_barrier_wait(g_barrier2);
34*99451b44SJordan Rupprecht 
35*99451b44SJordan Rupprecht     // Return
36*99451b44SJordan Rupprecht     return NULL;
37*99451b44SJordan Rupprecht }
38*99451b44SJordan Rupprecht 
39*99451b44SJordan Rupprecht void *
wait_thread_func()40*99451b44SJordan Rupprecht wait_thread_func ()
41*99451b44SJordan Rupprecht {
42*99451b44SJordan Rupprecht     // Wait until the entire first group of threads is running
43*99451b44SJordan Rupprecht     pseudo_barrier_wait(g_barrier1);
44*99451b44SJordan Rupprecht 
45*99451b44SJordan Rupprecht     // Wait until the breakpoint has been passed
46*99451b44SJordan Rupprecht     pseudo_barrier_wait(g_barrier2);
47*99451b44SJordan Rupprecht 
48*99451b44SJordan Rupprecht     // Return
49*99451b44SJordan Rupprecht     return NULL;
50*99451b44SJordan Rupprecht }
51*99451b44SJordan Rupprecht 
52*99451b44SJordan Rupprecht void *
join_thread_func(void * input)53*99451b44SJordan Rupprecht join_thread_func (void *input)
54*99451b44SJordan Rupprecht {
55*99451b44SJordan Rupprecht     std::thread *thread_to_join = (std::thread *)input;
56*99451b44SJordan Rupprecht 
57*99451b44SJordan Rupprecht     // Sync up with the rest of the threads.
58*99451b44SJordan Rupprecht     pseudo_barrier_wait(g_barrier1);
59*99451b44SJordan Rupprecht 
60*99451b44SJordan Rupprecht     // Join the other thread
61*99451b44SJordan Rupprecht     thread_to_join->join();
62*99451b44SJordan Rupprecht 
63*99451b44SJordan Rupprecht     // Return
64*99451b44SJordan Rupprecht     return NULL;
65*99451b44SJordan Rupprecht }
66*99451b44SJordan Rupprecht 
main()67*99451b44SJordan Rupprecht int main ()
68*99451b44SJordan Rupprecht {
69*99451b44SJordan Rupprecht     // The first barrier waits for the non-joining threads to start.
70*99451b44SJordan Rupprecht     // This thread will also participate in that barrier.
71*99451b44SJordan Rupprecht     // The idea here is to guarantee that the joining thread will be
72*99451b44SJordan Rupprecht     // last in the internal list maintained by the debugger.
73*99451b44SJordan Rupprecht     pseudo_barrier_init(g_barrier1, 5);
74*99451b44SJordan Rupprecht 
75*99451b44SJordan Rupprecht     // The second barrier keeps the waiting threads around until the breakpoint
76*99451b44SJordan Rupprecht     // has been passed.
77*99451b44SJordan Rupprecht     pseudo_barrier_init(g_barrier2, 4);
78*99451b44SJordan Rupprecht 
79*99451b44SJordan Rupprecht     // Create a thread to hit the breakpoint
80*99451b44SJordan Rupprecht     std::thread thread_1(break_thread_func);
81*99451b44SJordan Rupprecht 
82*99451b44SJordan Rupprecht     // Create more threads to slow the debugger down during processing.
83*99451b44SJordan Rupprecht     std::thread thread_2(wait_thread_func);
84*99451b44SJordan Rupprecht     std::thread thread_3(wait_thread_func);
85*99451b44SJordan Rupprecht     std::thread thread_4(wait_thread_func);
86*99451b44SJordan Rupprecht 
87*99451b44SJordan Rupprecht     // Create a thread to join the breakpoint thread
88*99451b44SJordan Rupprecht     std::thread thread_5(join_thread_func, &thread_1);
89*99451b44SJordan Rupprecht 
90*99451b44SJordan Rupprecht     // Wait for the threads to finish
91*99451b44SJordan Rupprecht     thread_5.join();  // implies thread_1 is already finished
92*99451b44SJordan Rupprecht     thread_4.join();
93*99451b44SJordan Rupprecht     thread_3.join();
94*99451b44SJordan Rupprecht     thread_2.join();
95*99451b44SJordan Rupprecht 
96*99451b44SJordan Rupprecht     return 0;
97*99451b44SJordan Rupprecht }
98