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; }