1 /**
2  * Integration with and wrapper around ae.sys.shutdown
3  * for networked (ae.net.asockets-based) applications.
4  *
5  * Unlike ae.sys.shutdown, the handlers are called from
6  * within the same thread they were registered from -
7  * provided that socketManager.loop() is running in that
8  * thread.
9  *
10  * License:
11  *   This Source Code Form is subject to the terms of
12  *   the Mozilla Public License, v. 2.0. If a copy of
13  *   the MPL was not distributed with this file, You
14  *   can obtain one at http://mozilla.org/MPL/2.0/.
15  *
16  * Authors:
17  *   Vladimir Panteleev <vladimir@thecybershadow.net>
18  */
19 
20 // TODO: Unify addShutdownHandler under a common API.
21 // The host program should decide which shutdown
22 // driver to use.
23 
24 // TODO: Add shuttingDown property
25 
26 module ae.net.shutdown;
27 
28 void addShutdownHandler(void delegate(scope const(char)[] reason) fn)
29 {
30 	handlers ~= fn;
31 	if (handlers.length == 1) // first
32 		register();
33 }
34 
35 deprecated void addShutdownHandler(void delegate() fn)
36 {
37 	addShutdownHandler((scope const(char)[] reason) { fn(); });
38 }
39 
40 /// Calls all registered handlers.
41 void shutdown(scope const(char)[] reason)
42 {
43 	foreach_reverse (fn; handlers)
44 		fn(reason);
45 }
46 
47 deprecated void shutdown()
48 {
49 	shutdown(null);
50 }
51 
52 private:
53 
54 static import ae.sys.shutdown;
55 import std.socket : socketPair;
56 import ae.net.asockets;
57 import ae.sys.data;
58 
59 // Per-thread
60 void delegate(scope const(char)[] reason)[] handlers;
61 
62 final class ShutdownConnection : TcpConnection
63 {
64 	Socket pinger;
65 
66 	this()
67 	{
68 		auto pair = socketPair();
69 		pair[0].blocking = false;
70 		super(pair[0]);
71 		pinger = pair[1];
72 		this.handleReadData = &onReadData;
73 		addShutdownHandler(&onShutdown); // for manual shutdown calls
74 		this.daemonRead = true;
75 	}
76 
77 	void ping(scope const(char)[] reason) //@nogc
78 	{
79 		static immutable ubyte[1] nullReason = [0];
80 		pinger.send(reason.length ? cast(ubyte[])reason : nullReason[]);
81 	}
82 
83 	void onShutdown(scope const(char)[] reason)
84 	{
85 		pinger.close();
86 	}
87 
88 	void onReadData(Data data)
89 	{
90 		auto dataBytes = cast(char[])data.contents;
91 		auto reason = dataBytes.length == 1 && dataBytes[0] == 0 ? null : dataBytes;
92 		shutdown(reason);
93 	}
94 }
95 
96 void register()
97 {
98 	auto socket = new ShutdownConnection();
99 	ae.sys.shutdown.addShutdownHandler(&socket.ping);
100 }