//===-- main.cpp ------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // This test is intended to create a situation in which one thread will exit // while a breakpoint is being handled in another thread. This may not always // happen because it's possible that the exiting thread will exit before the // breakpoint is hit. The test case should be flexible enough to treat that // as success. #include #include #include volatile int g_test = 0; // Note that although hogging the CPU while waiting for a variable to change // would be terrible in production code, it's great for testing since it // avoids a lot of messy context switching to get multiple threads synchronized. #define do_nothing() #define pseudo_barrier_wait(bar) \ --bar; \ while (bar > 0) \ do_nothing(); #define pseudo_barrier_init(bar, count) (bar = count) // A barrier to synchronize all the threads. std::atomic_int g_barrier1; // A barrier to keep the threads from exiting until after the breakpoint has // been passed. std::atomic_int g_barrier2; void * break_thread_func () { // Wait until all the threads are running pseudo_barrier_wait(g_barrier1); // Wait for the join thread to join std::this_thread::sleep_for(std::chrono::microseconds(50)); // Do something g_test++; // Set breakpoint here // Synchronize after the breakpoint pseudo_barrier_wait(g_barrier2); // Return return NULL; } void * wait_thread_func () { // Wait until the entire first group of threads is running pseudo_barrier_wait(g_barrier1); // Wait until the breakpoint has been passed pseudo_barrier_wait(g_barrier2); // Return return NULL; } void * join_thread_func (void *input) { std::thread *thread_to_join = (std::thread *)input; // Sync up with the rest of the threads. pseudo_barrier_wait(g_barrier1); // Join the other thread thread_to_join->join(); // Return return NULL; } int main () { // The first barrier waits for the non-joining threads to start. // This thread will also participate in that barrier. // The idea here is to guarantee that the joining thread will be // last in the internal list maintained by the debugger. pseudo_barrier_init(g_barrier1, 5); // The second barrier keeps the waiting threads around until the breakpoint // has been passed. pseudo_barrier_init(g_barrier2, 4); // Create a thread to hit the breakpoint std::thread thread_1(break_thread_func); // Create more threads to slow the debugger down during processing. std::thread thread_2(wait_thread_func); std::thread thread_3(wait_thread_func); std::thread thread_4(wait_thread_func); // Create a thread to join the breakpoint thread std::thread thread_5(join_thread_func, &thread_1); // Wait for the threads to finish thread_5.join(); // implies thread_1 is already finished thread_4.join(); thread_3.join(); thread_2.join(); return 0; }