79 lines · cpp
1#include "pseudo_barrier.h"2#include "thread.h"3#include <atomic>4#include <chrono>5#include <cinttypes>6#include <csignal>7#include <cstdio>8#include <cstdlib>9#include <cstring>10#include <mutex>11#include <thread>12#include <unistd.h>13#include <vector>14 15pseudo_barrier_t barrier;16std::mutex print_mutex;17std::atomic<bool> can_work;18thread_local volatile sig_atomic_t can_exit_now = false;19 20static void sigint_handler(int signo) {}21 22static void sigusr1_handler(int signo) {23 std::lock_guard<std::mutex> lock{print_mutex};24 std::printf("received SIGUSR1 on thread id: %" PRIx64 "\n", get_thread_id());25 can_exit_now = true;26}27 28static void thread_func() {29 // this ensures that all threads start before we stop30 pseudo_barrier_wait(barrier);31 32 // wait till the main thread indicates that we can go33 // (note: using a mutex here causes hang on FreeBSD when another thread34 // is suspended)35 while (!can_work.load())36 std::this_thread::sleep_for(std::chrono::milliseconds(50));37 38 // the mutex guarantees that two writes don't get interspersed39 {40 std::lock_guard<std::mutex> lock{print_mutex};41 std::printf("thread %" PRIx64 " running\n", get_thread_id());42 }43 44 // give other threads a fair chance to run45 for (int i = 0; i < 5; ++i) {46 std::this_thread::yield();47 std::this_thread::sleep_for(std::chrono::milliseconds(200));48 if (can_exit_now)49 return;50 }51 52 // if we didn't get signaled, terminate the program explicitly.53 _exit(0);54}55 56int main(int argc, char **argv) {57 int num = atoi(argv[1]);58 59 pseudo_barrier_init(barrier, num + 1);60 61 signal(SIGINT, sigint_handler);62 signal(SIGUSR1, sigusr1_handler);63 64 std::vector<std::thread> threads;65 for (int i = 0; i < num; ++i)66 threads.emplace_back(thread_func);67 68 // use the barrier to make sure all threads start before we stop69 pseudo_barrier_wait(barrier);70 std::raise(SIGINT);71 72 // allow the threads to work73 can_work.store(true);74 75 for (std::thread &thread : threads)76 thread.join();77 return 0;78}79