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 <vladimir@thecybershadow.net>
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 alias void delegate() nothrow @system SignalHandler;
22 
23 // https://github.com/D-Programming-Language/druntime/pull/759
24 version(OSX) private
25 {
26 	enum SIG_BLOCK   = 1;
27 	enum SIG_UNBLOCK = 2;
28 	enum SIG_SETMASK = 3;
29 }
30 
31 // https://github.com/D-Programming-Language/druntime/pull/1140
32 version(FreeBSD) private
33 {
34 	enum SIG_BLOCK   = 1;
35 	enum SIG_UNBLOCK = 2;
36 	enum SIG_SETMASK = 3;
37 }
38 
39 void addSignalHandler(int signum, SignalHandler fn)
40 {
41 	if (handlers[signum].length == 0)
42 	{
43 		alias sigfn_t = typeof(signal(0, null));
44 		auto old = signal(signum, cast(sigfn_t)&sighandle);
45 		assert(old == SIG_DFL, "A signal handler was already set");
46 	}
47 	handlers[signum] ~= fn;
48 }
49 
50 void removeSignalHandler(int signum, SignalHandler fn)
51 {
52 	foreach (i, lfn; handlers[signum])
53 		if (lfn is fn)
54 		{
55 			handlers[signum] = handlers[signum][0..i] ~ handlers[signum][i+1..$];
56 			if (handlers[signum].length == 0)
57 				signal(signum, SIG_DFL);
58 			return;
59 		}
60 	assert(0);
61 }
62 
63 // ***************************************************************************
64 
65 /// If the signal signum is raised during execution of code,
66 /// ignore it. Returns true if the signal was raised.
67 bool collectSignal(int signum, void delegate() code)
68 {
69 	sigset_t mask;
70 	sigemptyset(&mask);
71 	sigaddset(&mask, signum);
72 	errnoEnforce(pthread_sigmask(SIG_BLOCK, &mask, null) != -1);
73 
74 	bool result;
75 	{
76 		scope(exit)
77 			errnoEnforce(pthread_sigmask(SIG_UNBLOCK, &mask, null) != -1);
78 
79 		scope(exit)
80 		{
81 			static if (is(typeof(&sigpending)))
82 			{
83 				errnoEnforce(sigpending(&mask) == 0);
84 				auto m = sigismember(&mask, signum);
85 				errnoEnforce(m >= 0);
86 				result = m != 0;
87 				if (result)
88 				{
89 					int s;
90 					errnoEnforce(sigwait(&mask, &s) == 0);
91 					assert(s == signum);
92 				}
93 			}
94 			else
95 			{
96 				timespec zerotime;
97 				result = sigtimedwait(&mask, null, &zerotime) == 0;
98 			}
99 		}
100 
101 		code();
102 	}
103 
104 	return result;
105 }
106 
107 private:
108 
109 enum SIGMAX = 100;
110 shared SignalHandler[][SIGMAX] handlers;
111 
112 extern(C) void sighandle(int signum) nothrow @system
113 {
114 	if (signum >= 0 && signum < handlers.length)
115 		foreach (fn; handlers[signum])
116 			fn();
117 }