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 (!registered) 36 register(); 37 } 38 39 deprecated void addShutdownHandler(void delegate() fn) 40 { 41 addShutdownHandler((scope const(char)[] /*reason*/) { fn(); }); 42 } /// ditto 43 44 /// Remove a previously-registered handler. 45 void removeShutdownHandler(void delegate(scope const(char)[] reason) fn) 46 { 47 foreach (i, handler; handlers) 48 if (fn is handler) 49 { 50 handlers = handlers[0 .. i] ~ handlers[i+1 .. $]; 51 return; 52 } 53 assert(false, "No such shutdown handler registered"); 54 } 55 56 /// Calls all registered handlers. 57 void shutdown(scope const(char)[] reason) 58 { 59 foreach_reverse (fn; handlers) 60 fn(reason); 61 } 62 63 deprecated void shutdown() 64 { 65 shutdown(null); 66 } 67 68 private: 69 70 static import ae.sys.shutdown; 71 import std.socket : socketPair; 72 import ae.net.asockets; 73 import ae.sys.data; 74 75 // Per-thread 76 void delegate(scope const(char)[] reason)[] handlers; 77 bool registered; 78 79 final class ShutdownConnection : TcpConnection 80 { 81 Socket pinger; 82 83 this() 84 { 85 auto pair = socketPair(); 86 pair[0].blocking = false; 87 super(pair[0]); 88 pinger = pair[1]; 89 this.handleReadData = &onReadData; 90 addShutdownHandler(&onShutdown); // for manual shutdown calls 91 this.daemonRead = true; 92 } 93 94 void ping(scope const(char)[] reason) //@nogc 95 { 96 static immutable ubyte[1] nullReason = [0]; 97 pinger.send(reason.length ? cast(ubyte[])reason : nullReason[]); 98 } 99 100 void onShutdown(scope const(char)[] reason) 101 { 102 pinger.close(); 103 } 104 105 void onReadData(Data data) 106 { 107 data.asDataOf!char.enter((scope dataBytes) { 108 auto reason = dataBytes.length == 1 && dataBytes[0] == 0 ? null : dataBytes; 109 shutdown(reason); 110 }); 111 } 112 } 113 114 void register() 115 { 116 registered = true; 117 auto socket = new ShutdownConnection(); 118 ae.sys.shutdown.addShutdownHandler(&socket.ping); 119 }