1 /**
2 * POSIX signal handlers.
3 *
4 * License:
5 * This Source Code Form is subject to the terms of
6 * the Mozilla Public License, v. 2.0. If a copy of
7 * the MPL was not distributed with this file, You
8 * can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * Authors:
11 * Vladimir Panteleev <ae@cy.md>
12 */
13
14 module ae.sys.signals;
15 version(Posix):
16
17 import std.exception;
18
19 public import core.sys.posix.signal;
20
21 /// Handler callback type.
22 alias void delegate() nothrow @system SignalHandler;
23
24 // https://github.com/D-Programming-Language/druntime/pull/759
25 version(OSX) private
26 {
27 enum SIG_BLOCK = 1;
28 enum SIG_UNBLOCK = 2;
29 enum SIG_SETMASK = 3;
30 }
31
32 // https://github.com/D-Programming-Language/druntime/pull/1140
33 version(FreeBSD) private
34 {
35 enum SIG_BLOCK = 1;
36 enum SIG_UNBLOCK = 2;
37 enum SIG_SETMASK = 3;
38 }
39
40 /// Register a handler for a POSIX signal.
41 void addSignalHandler(int signum, SignalHandler fn)
42 {
43 handlers[signum].add(fn, {
44 alias sigfn_t = typeof(signal(0, null));
45 auto old = signal(signum, cast(sigfn_t)&sighandle);
46 assert(old == SIG_DFL || old == SIG_IGN, "A signal handler was already set");
47 });
48 }
49
50 /// Unregister a previously registered signal handler.
51 void removeSignalHandler(int signum, SignalHandler fn)
52 {
53 handlers[signum].remove(fn, {
54 signal(signum, SIG_DFL);
55 });
56 }
57
58 // ***************************************************************************
59
60 /// If the signal signum is raised during execution of code,
61 /// ignore it. Returns true if the signal was raised.
62 bool collectSignal(int signum, void delegate() code)
63 {
64 sigset_t mask;
65 sigemptyset(&mask);
66 sigaddset(&mask, signum);
67 errnoEnforce(pthread_sigmask(SIG_BLOCK, &mask, null) != -1);
68
69 bool result;
70 {
71 scope(exit)
72 errnoEnforce(pthread_sigmask(SIG_UNBLOCK, &mask, null) != -1);
73
74 scope(exit)
75 {
76 static if (is(typeof(&sigpending)))
77 {
78 errnoEnforce(sigpending(&mask) == 0);
79 auto m = sigismember(&mask, signum);
80 errnoEnforce(m >= 0);
81 result = m != 0;
82 if (result)
83 {
84 int s;
85 errnoEnforce(sigwait(&mask, &s) == 0);
86 assert(s == signum);
87 }
88 }
89 else
90 {
91 timespec zerotime;
92 result = sigtimedwait(&mask, null, &zerotime) == 0;
93 }
94 }
95
96 code();
97 }
98
99 return result;
100 }
101
102 private:
103
104 enum SIGMAX = 100;
105
106 synchronized class HandlerSet
107 {
108 alias T = SignalHandler;
109 private T[] handlers;
110
111 void add(T fn, scope void delegate() register)
112 {
113 if (handlers.length == 0)
114 register();
115 handlers ~= cast(shared)fn;
116 }
117 void remove(T fn, scope void delegate() deregister)
118 {
119 foreach (i, lfn; handlers)
120 if (lfn is fn)
121 {
122 handlers = handlers[0..i] ~ handlers[i+1..$];
123 if (handlers.length == 0)
124 deregister();
125 return;
126 }
127 assert(0);
128 }
129 const(T)[] get() pure nothrow @nogc { return cast(const(T[]))handlers; }
130 }
131
132 shared HandlerSet[SIGMAX] handlers;
133
134 shared static this() { foreach (ref h; handlers) h = new HandlerSet; }
135
136 extern(C) void sighandle(int signum) nothrow @system
137 {
138 if (signum >= 0 && signum < handlers.length)
139 foreach (fn; handlers[signum].get())
140 fn();
141 }