You can do surprising stuff in signal handlers; a program I used to work on had an interrupt menu in the signal handler. You could ctrl-c it and choose various "stop", "checkpoint", "dump status info" options. Similar to https://news.ycombinator.com/item?id=37899269 although without going quite so far as to adjust the stack with longjmp(). Whether you're "allowed" to do any of these things is another matter and they're almost certainly undefined behavior.
The interaction between threads and signals is really dire though. You need to mess around with signal masking. I think we'd ended up with an architecture where one thread caught the SIGINT and passed a SIGRT to a different thread; this behaved better in some way I cannot now remember.
If you want your program to be portable to Windows and MacOS as well, prepare for things to be differently broken there.
The interaction between threads and signals is really dire though. You need to mess around with signal masking. I think we'd ended up with an architecture where one thread caught the SIGINT and passed a SIGRT to a different thread; this behaved better in some way I cannot now remember.
If you want your program to be portable to Windows and MacOS as well, prepare for things to be differently broken there.