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 <ae@cy.md> 19 */ 20 21 module ae.sys.shutdown; 22 23 /// Register a handler to be called when a shutdown is requested. 24 /// Warning: the delegate may be called in an arbitrary thread. 25 void addShutdownHandler(void delegate(scope const(char)[] reason) fn) 26 { 27 handlers.add(fn); 28 } 29 30 deprecated void addShutdownHandler(void delegate() fn) 31 { 32 addShutdownHandler((scope const(char)[] reason) { fn(); }); 33 } 34 35 /// Calls all registered handlers. 36 void shutdown(scope const(char)[] reason) 37 { 38 foreach (fn; handlers.get()) 39 fn(reason); 40 } 41 42 deprecated void shutdown() 43 { 44 shutdown(null); 45 } 46 47 private: 48 49 import core.thread; 50 51 void syncShutdown(scope const(char)[] reason) nothrow @system 52 { 53 try 54 { 55 thread_suspendAll(); 56 scope(exit) thread_resumeAll(); 57 shutdown(reason); 58 } 59 catch (Throwable e) 60 { 61 import core.stdc.stdio; 62 static if (__VERSION__ < 2068) 63 { 64 string s = e.msg; 65 fprintf(stderr, "Unhandled error while shutting down:\r\n%.*s", s.length, s.ptr); 66 } 67 else 68 { 69 fprintf(stderr, "Unhandled error while shutting down:\r\n"); 70 _d_print_throwable(e); 71 } 72 } 73 } 74 75 // https://issues.dlang.org/show_bug.cgi?id=7016 76 version(Posix) 77 import ae.sys.signals; 78 else 79 version(Windows) 80 import core.sys.windows.windows; 81 82 extern (C) void _d_print_throwable(Throwable t) nothrow; 83 84 void register() 85 { 86 version(Posix) 87 { 88 addSignalHandler(SIGTERM, { syncShutdown("SIGTERM"); }); 89 addSignalHandler(SIGINT , { syncShutdown("SIGINT" ); }); 90 } 91 else 92 version(Windows) 93 { 94 static shared bool closing = false; 95 96 static void win32write(string msg) nothrow 97 { 98 DWORD written; 99 WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), msg.ptr, cast(uint)msg.length, &written, null); 100 } 101 102 extern(Windows) 103 static BOOL handlerRoutine(DWORD dwCtrlType) nothrow 104 { 105 if (!closing) 106 { 107 closing = true; 108 win32write("Shutdown event received, shutting down.\r\n"); 109 110 string reason; 111 switch (dwCtrlType) 112 { 113 case CTRL_C_EVENT : reason = "CTRL_C_EVENT" ; break; 114 case CTRL_BREAK_EVENT : reason = "CTRL_BREAK_EVENT" ; break; 115 case CTRL_CLOSE_EVENT : reason = "CTRL_CLOSE_EVENT" ; break; 116 case CTRL_LOGOFF_EVENT : reason = "CTRL_LOGOFF_EVENT" ; break; 117 case CTRL_SHUTDOWN_EVENT: reason = "CTRL_SHUTDOWN_EVENT"; break; 118 default: reason = "Unknown dwCtrlType"; break; 119 } 120 121 try 122 { 123 thread_attachThis(); 124 syncShutdown(reason); 125 thread_detachThis(); 126 127 return TRUE; 128 } 129 catch (Throwable e) 130 { 131 win32write("Unhandled error while shutting down:\r\n"); 132 static if (__VERSION__ < 2068) 133 win32write(e.msg); 134 else 135 _d_print_throwable(e); 136 } 137 } 138 return FALSE; 139 } 140 141 // https://issues.dlang.org/show_bug.cgi?id=12710 142 SetConsoleCtrlHandler(cast(PHANDLER_ROUTINE)&handlerRoutine, TRUE); 143 } 144 } 145 146 synchronized class HandlerSet 147 { 148 alias T = void delegate(scope const(char)[] reason); 149 private T[] handlers; 150 151 void add(T fn) 152 { 153 if (handlers.length == 0) 154 register(); 155 handlers ~= cast(shared)fn; 156 } 157 const(T)[] get() { return cast(const(T[]))handlers; } 158 } 159 160 shared HandlerSet handlers; 161 shared static this() { handlers = new HandlerSet; }