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