Thread overview
Linux signal handling - notifying a condition variable
Mar 15, 2018
Jim King
Mar 15, 2018
Adam D. Ruppe
Mar 15, 2018
Jim King
Mar 15, 2018
Adam D. Ruppe
Mar 15, 2018
Adam D. Ruppe
Mar 15, 2018
Dmitry Olshansky
Mar 15, 2018
Patrick Schluter
Mar 15, 2018
Dmitry Olshansky
Mar 15, 2018
Patrick Schluter
Mar 16, 2018
James E. King III
March 15, 2018
I am trying to add graceful shutdown support to a test harness.  In the test harness, a server class consumes a thread to accept connections and service them.  In order to stop the server, it has to be interrupted.  This interruption mechanism is based on core.sync.condition.

I want to add a signal handler so that if SIGINT is received, the server is interrupted and stops gracefully.

In going through the signal documentation it looks like the signal handler must be a "nothrow @nogc" variety.  Makes sense - there's very little you can do in a signal handler.

I tried carrying this down through to a class that ends up calling core.sync.condition.Condition.notifyAll(), however that method is not declared as "nothrow @nogc", therefore I assume that means I cannot use core.sync.condition.Condition from a signal handler.  It would seem to me that a notify mechanism in a condition variable implementation perhaps could throw, but I wouldn't expect it to do any garbage collection.

How is one supposed to intercept SIGINT and notify a condition variable?  Is this a deficiency in the standard library?

- Jim
March 15, 2018
On Thursday, 15 March 2018 at 16:51:59 UTC, Jim King wrote:
> In going through the signal documentation it looks like the signal handler must be a "nothrow @nogc" variety.

Looks like notify actually can throw an exception... the way I usually do signal handlers is just set a global variable:

__global bool interrupted = false;
void sigint_handler() { interrupted = true; }

then in the main loop check that periodically... idk if that'd work well withthe condition variable though, I have never actually used that...

March 15, 2018
On Thursday, 15 March 2018 at 17:12:24 UTC, Adam D. Ruppe wrote:
> On Thursday, 15 March 2018 at 16:51:59 UTC, Jim King wrote:
>> In going through the signal documentation it looks like the signal handler must be a "nothrow @nogc" variety.
>
> Looks like notify actually can throw an exception... the way I usually do signal handlers is just set a global variable:
>
> __global bool interrupted = false;
> void sigint_handler() { interrupted = true; }
>
> then in the main loop check that periodically... idk if that'd work well withthe condition variable though, I have never actually used that...

Thanks for that suggestion; I was in the middle of implementing that.  The problem with that is that it requires a busy loop to detect it.  It is far better to block on condition variables than to have a busy loop.

I agree condition variable notify could throw if the condition variable is not in the correct state.  I think it would be possible for someone to catch this and make sure it does not throw, but the "@nogc" part of it seems a bit more involved.  Condition variables (and mutexes) are supposed to be usable from a signal handler.

March 15, 2018
On Thursday, 15 March 2018 at 17:30:50 UTC, Jim King wrote:
> The problem with that is that it requires a busy loop to detect it.

well, what's your thread doing? In the case I used this pattern, it was blocking on a call to select() anyway, so when it returned with EINTR, that was a good opportunity to check the flag before waiting for the next event.

But of course that isn't always going to be the case...

> I think it would be possible for someone to catch this and make sure it does not throw, but the "@nogc" part of it seems a bit more involved.  Condition variables (and mutexes) are supposed to be usable from a signal handler.

Yeah, I think this is just an oversight. See, the throw on error previously didn't work well with @nogc (you could do it, but the catcher would have to free the exception object which most code wouldn't do) and the newest dmd version changes that.

But the library developer would still have to verify it and add the @nogc annotation themselves.

Of course, it is still throwing, but you can catch in the signal handler to make it nothrow at that point.


So I'd say probably file a bug and/or open a PR yourself to add the @nogc annotation to those methods and it'll probably work next release.
March 15, 2018
Alternatively btw you can use the pthreads C functions

   import core.sys.posix.pthread;

which shuld also be nogc right now. The condition class wraps those on Linux fairly thinly; using the C functions should be little more trouble.
March 15, 2018
On Thursday, 15 March 2018 at 17:30:50 UTC, Jim King wrote:
> On Thursday, 15 March 2018 at 17:12:24 UTC, Adam D. Ruppe wrote:
>> On Thursday, 15 March 2018 at 16:51:59 UTC, Jim King wrote:
>>> In going through the signal documentation it looks like the signal handler must be a "nothrow @nogc" variety.
>>
>> Looks like notify actually can throw an exception... the way I usually do signal handlers is just set a global variable:
>>
>> __global bool interrupted = false;
>> void sigint_handler() { interrupted = true; }
>>
>> then in the main loop check that periodically... idk if that'd work well withthe condition variable though, I have never actually used that...
>
> Thanks for that suggestion; I was in the middle of implementing that.  The problem with that is that it requires a busy loop to detect it.  It is far better to block on condition variables than to have a busy loop.

Another option if you are on linux is to use eventfd. Then you can trigger it with simple write on eventfd descriptor.

As far as waiting goes it’s either read on descriptor or poll/select.
f it seems a bit more involved.
> Condition variables (and mutexes) are supposed to be usable from a signal handler.

However I’d be super careful about mutexes with signal handlers. Really self-pipe trick or eventfd seems way more signal safe.



March 15, 2018
On Thursday, 15 March 2018 at 19:23:26 UTC, Dmitry Olshansky wrote:
> On Thursday, 15 March 2018 at 17:30:50 UTC, Jim King wrote:
>> [...]
>
> Another option if you are on linux is to use eventfd. Then you can trigger it with simple write on eventfd descriptor.
>
> As far as waiting goes it’s either read on descriptor or poll/select.
> f it seems a bit more involved.
>> [...]
>
> However I’d be super careful about mutexes with signal handlers. Really self-pipe trick or eventfd seems way more signal safe.

There's also signalfd which is quite practical to change signals in select/poll/epoll events.
March 15, 2018
On Thursday, 15 March 2018 at 19:36:44 UTC, Patrick Schluter wrote:
> On Thursday, 15 March 2018 at 19:23:26 UTC, Dmitry Olshansky wrote:
>> On Thursday, 15 March 2018 at 17:30:50 UTC, Jim King wrote:
>>> [...]
>>
>> Another option if you are on linux is to use eventfd. Then you can trigger it with simple write on eventfd descriptor.
>>
>> As far as waiting goes it’s either read on descriptor or poll/select.
>> f it seems a bit more involved.
>>> [...]
>>
>> However I’d be super careful about mutexes with signal handlers. Really self-pipe trick or eventfd seems way more signal safe.
>
> There's also signalfd which is quite practical to change signals in select/poll/epoll events.

Indeed, handling SIGINT termination in an event loop is easiest. Just need to mask the signal out, somewhat counter-intuitively.


March 15, 2018
On Thursday, 15 March 2018 at 16:51:59 UTC, Jim King wrote:
> I am trying to add graceful shutdown support to a test harness.
>  In the test harness, a server class consumes a thread to accept connections and service them.  In order to stop the server, it has to be interrupted.  This interruption mechanism is based on core.sync.condition.
>
> I want to add a signal handler so that if SIGINT is received, the server is interrupted and stops gracefully.
>

signalfd [1] is a good solution on Linux.

core.sys.linux.sys.signalfd;

[1]: http://man7.org/linux/man-pages/man2/signalfd.2.html
March 16, 2018
On Thursday, 15 March 2018 at 19:43:09 UTC, Patrick Schluter wrote:
> On Thursday, 15 March 2018 at 16:51:59 UTC, Jim King wrote:
>> I am trying to add graceful shutdown support to a test harness.
>>  In the test harness, a server class consumes a thread to accept connections and service them.  In order to stop the server, it has to be interrupted.  This interruption mechanism is based on core.sync.condition.
>>
>> I want to add a signal handler so that if SIGINT is received, the server is interrupted and stops gracefully.
>>
>
> signalfd [1] is a good solution on Linux.
>
> core.sys.linux.sys.signalfd;
>
> [1]: http://man7.org/linux/man-pages/man2/signalfd.2.html

Thank you, this looks like the best solution.  On further reading I found that
it is not safe to use condition variables from signal handlers, per the documentation in https://linux.die.net/man/3/pthread_cond_signal.