1 /** 2 * Application shutdown control (with SIGTERM handling). 3 * Different from atexit in that it controls initiation 4 * of graceful shutdown, as opposed to cleanup actions 5 * that are done as part of the shutdown process. 6 * 7 * Note: thread safety of this module is questionable. 8 * Use ae.net.shutdown for networked applications. 9 * TODO: transition to thread-safe centralized event loop. 10 * 11 * License: 12 * This Source Code Form is subject to the terms of 13 * the Mozilla Public License, v. 2.0. If a copy of 14 * the MPL was not distributed with this file, You 15 * can obtain one at http://mozilla.org/MPL/2.0/. 16 * 17 * Authors: 18 * Vladimir Panteleev <vladimir@thecybershadow.net> 19 */ 20 21 module ae.sys.shutdown; 22 23 /// Warning: the delegate may be called in an arbitrary thread. 24 void addShutdownHandler(void delegate(scope const(char)[] reason) fn) 25 { 26 handlers.add(fn); 27 } 28 29 deprecated void addShutdownHandler(void delegate() fn) 30 { 31 addShutdownHandler((scope const(char)[] reason) { fn(); }); 32 } 33 34 /// Calls all registered handlers. 35 void shutdown(scope const(char)[] reason) 36 { 37 foreach (fn; handlers.get()) 38 fn(reason); 39 } 40 41 deprecated void shutdown() 42 { 43 shutdown(null); 44 } 45 46 private: 47 48 import core.thread; 49 50 void syncShutdown(scope const(char)[] reason) nothrow @system 51 { 52 try 53 { 54 thread_suspendAll(); 55 scope(exit) thread_resumeAll(); 56 shutdown(reason); 57 } 58 catch (Throwable e) 59 { 60 import core.stdc.stdio; 61 static if (__VERSION__ < 2068) 62 { 63 string s = e.msg; 64 fprintf(stderr, "Unhandled error while shutting down:\r\n%.*s", s.length, s.ptr); 65 } 66 else 67 { 68 fprintf(stderr, "Unhandled error while shutting down:\r\n"); 69 _d_print_throwable(e); 70 } 71 } 72 } 73 74 // http://d.puremagic.com/issues/show_bug.cgi?id=7016 75 version(Posix) 76 import ae.sys.signals; 77 else 78 version(Windows) 79 import core.sys.windows.windows; 80 81 extern (C) void _d_print_throwable(Throwable t) nothrow; 82 83 void register() 84 { 85 version(Posix) 86 { 87 addSignalHandler(SIGTERM, { syncShutdown("SIGTERM"); }); 88 addSignalHandler(SIGINT , { syncShutdown("SIGINT" ); }); 89 } 90 else 91 version(Windows) 92 { 93 static shared bool closing = false; 94 95 static void win32write(string msg) nothrow 96 { 97 DWORD written; 98 WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), msg.ptr, cast(uint)msg.length, &written, null); 99 } 100 101 extern(Windows) 102 static BOOL handlerRoutine(DWORD dwCtrlType) nothrow 103 { 104 if (!closing) 105 { 106 closing = true; 107 win32write("Shutdown event received, shutting down.\r\n"); 108 109 string reason; 110 switch (dwCtrlType) 111 { 112 case CTRL_C_EVENT : reason = "CTRL_C_EVENT" ; break; 113 case CTRL_BREAK_EVENT : reason = "CTRL_BREAK_EVENT" ; break; 114 case CTRL_CLOSE_EVENT : reason = "CTRL_CLOSE_EVENT" ; break; 115 case CTRL_LOGOFF_EVENT : reason = "CTRL_LOGOFF_EVENT" ; break; 116 case CTRL_SHUTDOWN_EVENT: reason = "CTRL_SHUTDOWN_EVENT"; break; 117 default: reason = "Unknown dwCtrlType"; break; 118 } 119 120 try 121 { 122 thread_attachThis(); 123 syncShutdown(reason); 124 thread_detachThis(); 125 126 return TRUE; 127 } 128 catch (Throwable e) 129 { 130 win32write("Unhandled error while shutting down:\r\n"); 131 static if (__VERSION__ < 2068) 132 win32write(e.msg); 133 else 134 _d_print_throwable(e); 135 } 136 } 137 return FALSE; 138 } 139 140 // https://issues.dlang.org/show_bug.cgi?id=12710 141 SetConsoleCtrlHandler(cast(PHANDLER_ROUTINE)&handlerRoutine, TRUE); 142 } 143 } 144 145 synchronized class HandlerSet 146 { 147 alias T = void delegate(scope const(char)[] reason); 148 private T[] handlers; 149 150 void add(T fn) 151 { 152 if (handlers.length == 0) 153 register(); 154 handlers ~= cast(shared)fn; 155 } 156 const(T)[] get() { return cast(const(T[]))handlers; } 157 } 158 159 shared HandlerSet handlers; 160 shared static this() { handlers = new HandlerSet; }