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