www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - signal handling

reply "Danny" <danny.milo+a gmail.com> writes:
Hi,

if I want to clean up inside a signal handler and then exit the 
process (as it would have without me handling it), what do I do?

Can I exit() inside a signal handler or should I use a more 
direct "quit now" function? (after all, it could have been in the 
middle of relinking the free list when the signal arrived)

Do I reinstall the previous signal handler and then abort() ? If 
I want to store the previous signal handler, do I have to store 
in a shared variable ?

If I have multiple threads, what will happen to the other 
threads? Will it join() them and hang or will it kill them as 
well or will it just stop the thread that received the signal? 
Which thread did receive the signal?
Feb 06 2015
parent reply "rlonstein" <rlonstein+dlang gmail.com> writes:
On Friday, 6 February 2015 at 19:40:44 UTC, Danny wrote:
 Hi,

 if I want to clean up inside a signal handler and then exit the 
 process (as it would have without me handling it), what do I do?

 Can I exit() inside a signal handler or should I use a more 
 direct "quit now" function? (after all, it could have been in 
 the middle of relinking the free list when the signal arrived)
I'm using a basic C-style sigaction with core.sys.posix.signal and calling std.c.stdlib:exit. For context, I'm performing serial communication with an actual device so the signal may come when my code is blocked in an vendor API call or on an underlying syscall. I have very little state so I'm doing equally little beside freeing extern handles. Setting a global flag and testing it didn't really prove adequate and littered the checks everywhere so I'm thinking now of leveraging std.signals but I'm not sure that will be reliable.
Feb 07 2015
parent reply "Danny" <danny.milo+a gmail.com> writes:
Hi rlonstein,

I've now read up on the opengroup pages about signal handling 
(which references POSIX), and apparently it goes like this:

A signal can be delivered to a specific thread or to a process. 
If it's delivered to a process, a random thread of that process 
will receive it. If it's delivered to a thread, that thread will 
receive it. sigaction() thus seems to be required per-thread. The 
handler of a random thread will usually be called.

I've now settled on:
- per thread, register signal handler using sigaction with a mask 
that blocks all the cleanup signals while the handler is running. 
Remember the old handler in TLS variable.
- in the signal handler, clean up (just use the write syscall to 
make it sane), then use sigaction to reinstall the old signal 
handler, and then call raise().

Seems to work fine so far. Not sure whether it's safe to raise() 
inside a signal handler. Calling raise() without reinstalling the 
old signal handler is a very bad idea, I tried. Interesting is 
that, after reinstalling the old signal handler, I can raise() 
inside the signal handler even though it's blocked. It probably 
gets queued in the kernel for the thread. Then the handler 
returns and it processes the queue, calling the old handler. So 
win win?

See 
<http://svn.nomike.com/playground/trunk/L1/D/TUI/Terminalclient.d>, 
bottom. Note that I don't distinguish between whether I'm in the 
correct thread or not since for my use case it's not necessary. 
The FD is always the same and who cares who resets the terminal...

I have very little state so I'm doing
 equally little beside freeing extern handles.
Yeah, same for me, really.
 Setting a global flag and testing it didn't really prove 
 adequate and littered the checks everywhere
Yeah,I don't think that EINTR and flag-checking is even safe. What if you check the flag (and see nothing happened) and then go back to the loop and block (in read() or whatever), and right after the flag checking, unbeknowst to you the signal handler sets the flag, returns, and only then you block in read()? You'd block forever. Do you know signalfd() ? I know how it is with external libaries, they always block at the silliest of times. But I've had one (OKAPI) which gave me the FD back, so I could select() on a bunch FDs in my mainloop. In that case, signalfd() was nice since it showed up as a normal "read ready" in select(), i.e. as a normal "event source". Might be worth a try in your case? Like this, in pseudo-code: while (!error) { auto readyfd = select([signalfd, externalfd]); if (readyfd == signalfd) ... signal (would have) happened, handle it else if (readyfd == externalfd) vendor_read_chunk(); else ... }
 so I'm thinking now of leveraging std.signals but I'm not sure 
 that will be reliable.
Hmm, I don't know that one yet.
Feb 07 2015
parent reply "rlonstein" <rlonstein+dlang gmail.com> writes:
On Saturday, 7 February 2015 at 18:30:00 UTC, Danny wrote:
     [snip]
 Seems to work fine so far. Not sure whether it's safe to 
 raise() inside a signal handler. Calling raise() without 
 reinstalling the old signal handler is a very bad idea, I
[snip] I'm not sure that it's really safe to call raise in the handler. Even C printf() is potentially unsafe leading to recommendations to use only re-entrant functions. I would not expect raise() to be safe (ex. never allocates memory or calls gc somewhere down the line). [snip]
 Yeah,I don't think that EINTR and flag-checking is even safe. 
 What if you check the flag (and see nothing happened) and then 
 go back to the loop and block (in read() or whatever), and 
 right after the flag checking, unbeknowst to you the signal 
 handler sets the flag, returns, and only then you block in 
 read()? You'd block forever.
Yes, pretty much.
 Do you know signalfd() ?
Yes, and I'm looking at using it.
 I know how it is with external libaries, they always block at 
 the silliest of times. But I've had one (OKAPI) which gave me 
 the FD back, so I could select() on a bunch FDs in my mainloop. 
 In that case, signalfd() was nice since it showed up as a 
 normal "read ready" in select(), i.e. as a normal "event 
 source". Might be worth a try in your case?
[snip]
 so I'm thinking now of leveraging std.signals but I'm not sure 
 that will be reliable.
Hmm, I don't know that one yet.
Again, probably not safe in sigaction, but using D's slots/signals to communicate that a posix signal has been received and must be handled. Along with signalfd(), I expect it will let me trigger object destruction.
Feb 09 2015
parent "Danny" <danny.milo+a gmail.com> writes:
Hmmm...

Just found 
<https://www.securecoding.cert.org/confluence/display/seccode/void+SIG33-C.+Do+not+recursively+invoke+the+ra
se%28%29+function>, 
the bottom part "Compliant Solution (POSIX)" does raise() in the 
signal handler.

However, I can't find it in the POSIX standard at 
<http://pubs.opengroup.org/onlinepubs/009695399/functions/sigaction.html>.

But 
<https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+wit
in+signal+handlers> 
lists the async-safe functions. raise() and sigaction() are in 
it. But _Exit() is, too.
Feb 09 2015