The best thing you can do is set a global variable value and that’s it. Let your main even loop mind the value and proceed from there. Only do this in a single thread and block singles in all others as the first thing you do. Threads and signals do not mix otherwise.
You want signalfd, which may optionally fed to epoll or any of the other multiplexing syscalls.
Signalfd can mostly be implemented on any platform using a pipe (if you don't have to mix 32-bit and 64-bit processes, or if you don't need the siginfo payload, or if you read your kernel's documentation enough to figure out which "layout" of the union members is active - this is really hairy). Note however the major caveat of running out of pipe buffer.
A more-reliable alternative is to use an async-signal-safe allocator (e.g. an `mmap` wrapper) to atomically store the payloads, and only use a pipe as a flag for whether there's something to look for.
Of course, none of these mechanisms are useful for naturally synchronous signals, such as the `SIGSEGV` from dereferencing an invalid pointer, so the function-coloring approach still needs to be used.
> Another option is to use a proper OS that includes the ability to receive signals as a part of your main event loops
Every 'nix can do that. Your signal handler just writes a byte to a pipe and your main loop reads the pipe or fifo. The pipe/fifo is your event queue, which your main loop reads.
> The best thing you can do is set a global variable value and that’s it.
Seems kinda limiting.
If I've got a slow file download going on in one thread, and my program gets a Ctrl+C signal, waiting for the download to complete before I exit ain't exactly a great user experience.
Use select() or epoll() or kqueue() to see if your socket is ready for reading. That way you can monitor your global variable too. That’s the correct way to do it.
If you have multiple threads, you start one just to mind signals.
Signal handlers are extremely limited in what they can do, that’s the point. They are analogous to hardware interrupt handlers.
Another option is to use a proper OS that includes the ability to receive signals as a part of your main event loops: https://man.openbsd.org/kqueue.2#EVFILT_SIGNAL
I believe you can also do something similar with epoll() on Linux but not sure the semantics are quite as nice as kqueue.