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.