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 <ae@cy.md> 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 /// Register a handler to be called when a shutdown is requested. 29 /// The handler should close network connections and cancel timers, 30 /// thus removing all owned resources from the event loop which would 31 /// block it from exiting cleanly. 32 void addShutdownHandler(void delegate(scope const(char)[] reason) fn) 33 { 34 handlers ~= fn; 35 if (handlers.length == 1) // first 36 register(); 37 } 38 39 deprecated void addShutdownHandler(void delegate() fn) 40 { 41 addShutdownHandler((scope const(char)[] /*reason*/) { fn(); }); 42 } 43 44 /// Calls all registered handlers. 45 void shutdown(scope const(char)[] reason) 46 { 47 foreach_reverse (fn; handlers) 48 fn(reason); 49 } 50 51 deprecated void shutdown() 52 { 53 shutdown(null); 54 } 55 56 private: 57 58 static import ae.sys.shutdown; 59 import std.socket : socketPair; 60 import ae.net.asockets; 61 import ae.sys.data; 62 63 // Per-thread 64 void delegate(scope const(char)[] reason)[] handlers; 65 66 final class ShutdownConnection : TcpConnection 67 { 68 Socket pinger; 69 70 this() 71 { 72 auto pair = socketPair(); 73 pair[0].blocking = false; 74 super(pair[0]); 75 pinger = pair[1]; 76 this.handleReadData = &onReadData; 77 addShutdownHandler(&onShutdown); // for manual shutdown calls 78 this.daemonRead = true; 79 } 80 81 void ping(scope const(char)[] reason) //@nogc 82 { 83 static immutable ubyte[1] nullReason = [0]; 84 pinger.send(reason.length ? cast(ubyte[])reason : nullReason[]); 85 } 86 87 void onShutdown(scope const(char)[] reason) 88 { 89 pinger.close(); 90 } 91 92 void onReadData(Data data) 93 { 94 auto dataBytes = cast(char[])data.contents; 95 auto reason = dataBytes.length == 1 && dataBytes[0] == 0 ? null : dataBytes; 96 shutdown(reason); 97 } 98 } 99 100 void register() 101 { 102 auto socket = new ShutdownConnection(); 103 ae.sys.shutdown.addShutdownHandler(&socket.ping); 104 }