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 		string s = e.msg;
54 		fprintf(stderr, "Unhandled error while shutting down:\r\n%.*s", s.length, s.ptr);
55 	}
56 }
57 
58 // http://d.puremagic.com/issues/show_bug.cgi?id=7016
59 version(Posix)
60 	import ae.sys.signals;
61 else
62 version(Windows)
63 	import core.sys.windows.windows;
64 
65 void register()
66 {
67 	version(Posix)
68 	{
69 		addSignalHandler(SIGTERM, { syncShutdown(); });
70 		addSignalHandler(SIGINT , { syncShutdown(); });
71 	}
72 	else
73 	version(Windows)
74 	{
75 		static shared bool closing = false;
76 
77 		static void win32write(string msg) nothrow
78 		{
79 			DWORD written;
80 			WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), msg.ptr, cast(uint)msg.length, &written, null);
81 		}
82 
83 		extern(Windows)
84 		static BOOL handlerRoutine(DWORD dwCtrlType) nothrow
85 		{
86 			if (!closing)
87 			{
88 				closing = true;
89 				win32write("Shutdown event received, shutting down.\r\n");
90 
91 				try
92 				{
93 					thread_attachThis();
94 					syncShutdown();
95 					thread_detachThis();
96 
97 					return TRUE;
98 				}
99 				catch (Throwable e)
100 				{
101 					win32write("Unhandled error while shutting down:\r\n");
102 					win32write(e.msg);
103 				}
104 			}
105 			return FALSE;
106 		}
107 
108 		// https://issues.dlang.org/show_bug.cgi?id=12710
109 		SetConsoleCtrlHandler(cast(PHANDLER_ROUTINE)&handlerRoutine, TRUE);
110 	}
111 }
112 
113 shared void delegate()[] handlers;