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 }