104b766daSPavel Labath #include "pseudo_barrier.h"
204b766daSPavel Labath #include "thread.h"
3*9790406aSMichał Górny #include <atomic>
404b766daSPavel Labath #include <chrono>
504b766daSPavel Labath #include <cinttypes>
604b766daSPavel Labath #include <csignal>
70bbe2a3cSPavel Labath #include <cstdio>
897091866SPavel Labath #include <cstdlib>
904b766daSPavel Labath #include <cstring>
10*9790406aSMichał Górny #include <mutex>
1104b766daSPavel Labath #include <thread>
1204b766daSPavel Labath #include <unistd.h>
1304b766daSPavel Labath #include <vector>
1404b766daSPavel Labath 
1504b766daSPavel Labath pseudo_barrier_t barrier;
16*9790406aSMichał Górny std::mutex print_mutex;
17*9790406aSMichał Górny std::atomic<bool> can_work = ATOMIC_VAR_INIT(false);
18*9790406aSMichał Górny thread_local volatile sig_atomic_t can_exit_now = false;
19*9790406aSMichał Górny 
sigint_handler(int signo)20*9790406aSMichał Górny static void sigint_handler(int signo) {}
2104b766daSPavel Labath 
sigusr1_handler(int signo)2204b766daSPavel Labath static void sigusr1_handler(int signo) {
23*9790406aSMichał Górny   std::lock_guard<std::mutex> lock{print_mutex};
24*9790406aSMichał Górny   std::printf("received SIGUSR1 on thread id: %" PRIx64 "\n", get_thread_id());
25*9790406aSMichał Górny   can_exit_now = true;
2604b766daSPavel Labath }
2704b766daSPavel Labath 
thread_func()2804b766daSPavel Labath static void thread_func() {
29*9790406aSMichał Górny   // this ensures that all threads start before we stop
3004b766daSPavel Labath   pseudo_barrier_wait(barrier);
31*9790406aSMichał Górny 
32*9790406aSMichał Górny   // wait till the main thread indicates that we can go
33*9790406aSMichał Górny   // (note: using a mutex here causes hang on FreeBSD when another thread
34*9790406aSMichał Górny   // is suspended)
35*9790406aSMichał Górny   while (!can_work.load())
36*9790406aSMichał Górny     std::this_thread::sleep_for(std::chrono::milliseconds(50));
37*9790406aSMichał Górny 
38*9790406aSMichał Górny   // the mutex guarantees that two writes don't get interspersed
39*9790406aSMichał Górny   {
40*9790406aSMichał Górny     std::lock_guard<std::mutex> lock{print_mutex};
41b4f2d7cdSMichał Górny     std::printf("thread %" PRIx64 " running\n", get_thread_id());
4286e47231SMichał Górny   }
43*9790406aSMichał Górny 
44*9790406aSMichał Górny   // give other threads a fair chance to run
45*9790406aSMichał Górny   for (int i = 0; i < 5; ++i) {
46*9790406aSMichał Górny     std::this_thread::yield();
47*9790406aSMichał Górny     std::this_thread::sleep_for(std::chrono::milliseconds(200));
48*9790406aSMichał Górny     if (can_exit_now)
49*9790406aSMichał Górny       return;
50*9790406aSMichał Górny   }
51*9790406aSMichał Górny 
52*9790406aSMichał Górny   // if we didn't get signaled, terminate the program explicitly.
53*9790406aSMichał Górny   _exit(0);
5404b766daSPavel Labath }
5504b766daSPavel Labath 
main(int argc,char ** argv)5604b766daSPavel Labath int main(int argc, char **argv) {
5704b766daSPavel Labath   int num = atoi(argv[1]);
5804b766daSPavel Labath 
5904b766daSPavel Labath   pseudo_barrier_init(barrier, num + 1);
6004b766daSPavel Labath 
61*9790406aSMichał Górny   signal(SIGINT, sigint_handler);
6204b766daSPavel Labath   signal(SIGUSR1, sigusr1_handler);
6304b766daSPavel Labath 
6404b766daSPavel Labath   std::vector<std::thread> threads;
6504b766daSPavel Labath   for (int i = 0; i < num; ++i)
6604b766daSPavel Labath     threads.emplace_back(thread_func);
6704b766daSPavel Labath 
68*9790406aSMichał Górny   // use the barrier to make sure all threads start before we stop
6904b766daSPavel Labath   pseudo_barrier_wait(barrier);
70*9790406aSMichał Górny   std::raise(SIGINT);
7104b766daSPavel Labath 
72*9790406aSMichał Górny   // allow the threads to work
73*9790406aSMichał Górny   can_work.store(true);
7404b766daSPavel Labath 
7504b766daSPavel Labath   for (std::thread &thread : threads)
7604b766daSPavel Labath     thread.join();
7704b766daSPavel Labath   return 0;
7804b766daSPavel Labath }
79