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() fn) 25 { 26 handlers.add(fn); 27 } 28 29 /// Calls all registered handlers. 30 void shutdown() 31 { 32 foreach (fn; handlers.get()) 33 fn(); 34 } 35 36 private: 37 38 import core.thread; 39 40 void syncShutdown() nothrow @system 41 { 42 try 43 { 44 thread_suspendAll(); 45 scope(exit) thread_resumeAll(); 46 shutdown(); 47 } 48 catch (Throwable e) 49 { 50 import core.stdc.stdio; 51 static if (__VERSION__ < 2068) 52 { 53 string s = e.msg; 54 fprintf(stderr, "Unhandled error while shutting down:\r\n%.*s", s.length, s.ptr); 55 } 56 else 57 { 58 fprintf(stderr, "Unhandled error while shutting down:\r\n"); 59 _d_print_throwable(e); 60 } 61 } 62 } 63 64 // http://d.puremagic.com/issues/show_bug.cgi?id=7016 65 version(Posix) 66 import ae.sys.signals; 67 else 68 version(Windows) 69 import core.sys.windows.windows; 70 71 extern (C) void _d_print_throwable(Throwable t) nothrow; 72 73 void register() 74 { 75 version(Posix) 76 { 77 addSignalHandler(SIGTERM, { syncShutdown(); }); 78 addSignalHandler(SIGINT , { syncShutdown(); }); 79 } 80 else 81 version(Windows) 82 { 83 static shared bool closing = false; 84 85 static void win32write(string msg) nothrow 86 { 87 DWORD written; 88 WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), msg.ptr, cast(uint)msg.length, &written, null); 89 } 90 91 extern(Windows) 92 static BOOL handlerRoutine(DWORD dwCtrlType) nothrow 93 { 94 if (!closing) 95 { 96 closing = true; 97 win32write("Shutdown event received, shutting down.\r\n"); 98 99 try 100 { 101 thread_attachThis(); 102 syncShutdown(); 103 thread_detachThis(); 104 105 return TRUE; 106 } 107 catch (Throwable e) 108 { 109 win32write("Unhandled error while shutting down:\r\n"); 110 static if (__VERSION__ < 2068) 111 win32write(e.msg); 112 else 113 _d_print_throwable(e); 114 } 115 } 116 return FALSE; 117 } 118 119 // https://issues.dlang.org/show_bug.cgi?id=12710 120 SetConsoleCtrlHandler(cast(PHANDLER_ROUTINE)&handlerRoutine, TRUE); 121 } 122 } 123 124 synchronized class HandlerSet 125 { 126 alias T = void delegate(); 127 private T[] handlers; 128 129 void add(T fn) 130 { 131 if (handlers.length == 0) 132 register(); 133 handlers ~= cast(shared)fn; 134 } 135 const(T)[] get() { return cast(const(T[]))handlers; } 136 } 137 138 shared HandlerSet handlers; 139 shared static this() { handlers = new HandlerSet; }