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 }