llvm/lldb/test/API/functionalities/single-thread-step/main.cpp

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>

std::mutex mtx;
std::condition_variable cv;
int ready_thread_id = 0;
int signal_main_thread = 0;

void worker(int id) {
  std::cout << "Worker " << id << " executing..." << std::endl;

  // lldb test should change signal_main_thread to true to break the loop.
  while (!signal_main_thread) {
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }

  // Signal the main thread to continue main thread
  {
    std::lock_guard<std::mutex> lock(mtx);
    ready_thread_id = id; // break worker thread here
  }
  cv.notify_one();

  std::this_thread::sleep_for(std::chrono::seconds(1));
  std::cout << "Worker " << id << " finished." << std::endl;
}

void deadlock_func(std::unique_lock<std::mutex> &lock) {
  int i = 10;
  ++i;             // Set interrupt breakpoint here
  printf("%d", i); // Finish step-over from inner breakpoint
  auto func = [] { return ready_thread_id == 1; };
  cv.wait(lock, func);
}

int simulate_thread() {
  std::thread t1(worker, 1);

  std::unique_lock<std::mutex> lock(mtx);
  deadlock_func(lock); // Set breakpoint1 here

  std::thread t2(worker, 2); // Finish step-over from breakpoint1

  cv.wait(lock, [] { return ready_thread_id == 2; });

  t1.join();
  t2.join();

  std::cout << "Main thread continues..." << std::endl;

  return 0;
}

int bar() { return 54; }

int foo(const std::string p1, int extra) { return p1.size() + extra; }

int main(int argc, char *argv[]) {
  std::string ss = "this is a string for testing",
              ls = "this is a long string for testing";
  foo(ss.size() % 2 == 0 ? ss : ls, bar()); // Set breakpoint2 here

  simulate_thread(); // Finish step-over from breakpoint2

  return 0;
}