According to the man page (2) the exit
function is not thread safe : MT-Unsafe race:exit
, this is because this function tries to clean up resources (flush data to the disk, close file descriptors, etc...) by calling callbacks registered using on_exit
and atexit
. And I want my program to do that ! (one of my thread keeps a fd open during the whole program's lifespan so _exit
is not an option for me because I want all the data to be written to the output file)
My question is the following : if I'm being careful and I don't share any sensible data (like a fd) between my threads, is it "acceptable" to call exit
in a multi-threaded program ? Note that I'm only calling exit
if an unrecoverable error occurs. Yet, I can't afford having a segfault while the program tries to exit. The thing is, an unrecoverable error can happen from any thread...
I was thinking about using setjmp/longjmp to kill my threads "nicely" but this would be quite complex to do and would require many changes everywhere in my code.
Any suggestions would be greatly appreciated. Thanks ! :)
EDIT : Thanks to @Ctx enlightenment, I came up with the following idea :
#define EXIT(status) do { pthread_mutex_lock(&exit_mutex); exit(status); } while(0)
Of course the exit_mutex must be global (extern).
_exit
is not an option for me because I want all the data to be written to the output file Integer-type file descriptors do not buffer data in the process's address space. Anywrite()
(or similar) call that's completed when you call_exit()
will be in the file it was written to (barring things like power failures that crash the system before the page cache is flushed to disk...)exit()
only causes problems withFILE *
based streams that buffer data. – Quirinus_exit
is kind of gross anyway... Better clean what's cleanable :) – HyperkeratosisSIGKILL
or a power failure at any moment and be perfectly happy when it's restarted. You're spending how much time trying to make your cleanup robust? And even if you do that, you don't protect against a power failure or incorrectly aimedSIGKILL
. – Quirinus_exit
again, but the thing is I don't know every underlying Linux concepts and callingexit
safely instead of the brutal_exit
is a way for me to know I'm not making anything stupid and that resources will be cleaned up. Don't know if I'm making sense rn... :sweat_smile: – Hyperkeratosis_exit
? Because I'm using FILE* everywhere :x – Hyperkeratosisexit()
will flush all your openFILE *
streams. That can be a problem if, for example,FILE *
structures have internal locks of any kind and one thread is holding that lock for some reason. Then the call toexit()
will hang. (This is one reason whyexit()
is not async-signal-safe and can't be safely called from within a signal handler.) Can any of your uses of aFILE *
hold such a lock indefinitely? Perhaps by blocking for input on a read/writeFILE *
stream? – QuirinusO_NONBLOCK
flag when using fopen) but I don't see why it would prevent anexit()
from finishing... But tbh I don't really know how the FILE struct works internally, I might be wrong here. – Hyperkeratosisfread
must return data (fill the buffer) until EOF is reached, of course it will block until data is available. There is no way for this interface to even express "interrupted, please retry now" not "nothing for now, retry a bit later". Nor is there any way for an stdio write function to do that, but as long as the user buffer is copied elsewhere, and reusable by the user, the function doesn't need to block. Reading!=writing in this regard. Completed stdio write does not mean the data is on safe/stable storage. – Disfavorexit()
destroys the stdio data structures? – Disfavor_exit
be ok ? – Hyperkeratosisexit
. – Expeditiousexit
is pretty clean..._exit
isn't however. The only problem withexit
is its thread safety. – Hyperkeratosis