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;