1 /// Manage signals as reactor callbacks
2 module mecca.reactor.platform.kqueue_signals;
3 
4 // Licensed under the Boost license. Full copyright information in the AUTHORS file
5 
6 version (Kqueue):
7 package(mecca.reactor.platform):
8 
9 alias ReactorSignal = KqueueReactorSignal;
10 
11 private struct KqueueReactorSignal {
12     import core.sys.posix.signal : sigaction_t, sigaction, SIG_IGN;
13 
14     import std.traits : Parameters;
15 
16     import mecca.lib.exception : ASSERT, errnoEnforceNGC;
17     import mecca.platform.os : OSSignal;
18     import mecca.reactor.platform.kqueue : Kqueue;
19     import mecca.reactor.subsystems.poller : poller, Poller;
20 
21     alias SignalHandler = Kqueue.SignalHandler;
22 
23     private Poller.FdContext*[OSSignal.max + 1] handlers;
24     private sigaction_t[OSSignal.max + 1] previousActions;
25 
26     /**
27      * Must be called prior to registering any signals.
28      *
29      * Must be called after the reactor is open, and also after ReactorFS.openReactor has already been called.
30      */
31     void _open() @trusted @nogc {
32         ASSERT!"ReactorSignal.open called without first calling ReactorFD.openReactor"(poller.isOpen);
33     }
34 
35     /// Call this when shutting down the reactor. Mostly necessary for unit tests
36     void _close() @safe @nogc {
37         // noop
38     }
39 
40     /**
41      * register a signal handler
42      *
43      * Register a handler for a specific signal. The signal must not already be handled, either through ReactorSignal or
44      * otherwise.
45      *
46      * Params:
47      * signum = the signal to be handled
48      * handler = a delegate to be called when the signal arrives
49      */
50     void registerHandler(OSSignal signum, SignalHandler handler) @trusted @nogc {
51         ASSERT!"registerHandler called with invalid signal %s"(signum <= OSSignal.max || signum<=0, signum);
52         ASSERT!"signal %s registered twice"(handlers[signum] is null, signum);
53 
54         sigaction_t previousAction;
55         const sigaction_t action = { sa_handler: &dummySignalHandler };
56         errnoEnforceNGC(sigaction(signum, &action, &previousAction) == 0, "Failed to register signal action");
57 
58         handlers[signum] = poller.registerSignalHandler(signum, handler);
59         previousActions[signum] = previousAction;
60     }
61 
62     void registerHandler(string sig, T)(T handler) @trusted {
63         registerHandler(__traits(getMember, OSSignal, sig), (_){handler();});
64     }
65 
66     void unregisterHandler(OSSignal signum) @trusted @nogc {
67         ASSERT!"registerHandler called with invalid signal %s"(signum <= OSSignal.max || signum<=0, signum);
68         ASSERT!"signal %s not registered"(handlers[signum] !is null, signum);
69 
70         errnoEnforceNGC(sigaction(signum, &previousActions[signum], null) == 0, "Failed to restore signal action");
71         poller.unregisterSignalHandler(handlers[signum]);
72 
73         handlers[signum] = null;
74         previousActions[signum] = sigaction_t.init;
75     }
76 
77     void unregisterHandler(string sig)() @trusted @nogc {
78         unregisterHandler(__traits(getMember, OSSignal, sig));
79     }
80 
81     // Dummy signal handler is necessary, otherwise kqueue won't receive the
82     // signal since it has lower precedence
83     extern (C) private static void dummySignalHandler(int) {}
84 }