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 }