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 	handlers[signum].add(fn, {
42 		alias sigfn_t = typeof(signal(0, null));
43 		auto old = signal(signum, cast(sigfn_t)&sighandle);
44 		assert(old == SIG_DFL || old == SIG_IGN, "A signal handler was already set");
45 	});
46 }
47 
48 void removeSignalHandler(int signum, SignalHandler fn)
49 {
50 	handlers[signum].remove(fn, {
51 		signal(signum, SIG_DFL);
52 	});
53 }
54 
55 // ***************************************************************************
56 
57 /// If the signal signum is raised during execution of code,
58 /// ignore it. Returns true if the signal was raised.
59 bool collectSignal(int signum, void delegate() code)
60 {
61 	sigset_t mask;
62 	sigemptyset(&mask);
63 	sigaddset(&mask, signum);
64 	errnoEnforce(pthread_sigmask(SIG_BLOCK, &mask, null) != -1);
65 
66 	bool result;
67 	{
68 		scope(exit)
69 			errnoEnforce(pthread_sigmask(SIG_UNBLOCK, &mask, null) != -1);
70 
71 		scope(exit)
72 		{
73 			static if (is(typeof(&sigpending)))
74 			{
75 				errnoEnforce(sigpending(&mask) == 0);
76 				auto m = sigismember(&mask, signum);
77 				errnoEnforce(m >= 0);
78 				result = m != 0;
79 				if (result)
80 				{
81 					int s;
82 					errnoEnforce(sigwait(&mask, &s) == 0);
83 					assert(s == signum);
84 				}
85 			}
86 			else
87 			{
88 				timespec zerotime;
89 				result = sigtimedwait(&mask, null, &zerotime) == 0;
90 			}
91 		}
92 
93 		code();
94 	}
95 
96 	return result;
97 }
98 
99 private:
100 
101 enum SIGMAX = 100;
102 
103 synchronized class HandlerSet
104 {
105 	alias T = SignalHandler;
106 	private T[] handlers;
107 
108 	void add(T fn, scope void delegate() register)
109 	{
110 		if (handlers.length == 0)
111 			register();
112 		handlers ~= cast(shared)fn;
113 	}
114 	void remove(T fn, scope void delegate() deregister)
115 	{
116 		foreach (i, lfn; handlers)
117 			if (lfn is fn)
118 			{
119 				handlers = handlers[0..i] ~ handlers[i+1..$];
120 				if (handlers.length == 0)
121 					deregister();
122 				return;
123 			}
124 		assert(0);
125 	}
126 	const(T)[] get() pure nothrow @nogc { return cast(const(T[]))handlers; }
127 }
128 
129 shared HandlerSet[SIGMAX] handlers;
130 
131 shared static this() { foreach (ref h; handlers) h = new HandlerSet; }
132 
133 extern(C) void sighandle(int signum) nothrow @system
134 {
135 	if (signum >= 0 && signum < handlers.length)
136 		foreach (fn; handlers[signum].get())
137 			fn();
138 }