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 }