1 /******************************************************************************* 2 3 Registry implementation for multi-threaded access 4 5 This registry allows to look up a connection based on a `string`. 6 Conceptually, it can be seen as an equivalent to a DNS server, 7 as it turns symbolic names into "concrete" addresses (pointers). 8 9 It was originally part of the `std.concurrency` module, 10 but was extracted to make it reusable. 11 12 *******************************************************************************/ 13 14 module geod24.Registry; 15 16 import core.sync.mutex; 17 import geod24.concurrency; 18 import geod24.LocalRest; 19 20 /// Ditto 21 public shared struct Registry (API) 22 { 23 /// Map from a name to a connection. 24 /// Multiple names may point to the same connection. 25 private Listener!API[string] connections; 26 /// Gives all the names associated with a specific connection. 27 private string[][Listener!API] names; 28 private Mutex registryLock; 29 30 /// Initialize this registry, creating the Mutex 31 public void initialize () @safe nothrow 32 { 33 this.registryLock = new shared Mutex; 34 } 35 36 /** 37 * Gets the binding channel associated with `name`. 38 * 39 * Params: 40 * name = The name to locate within the registry. 41 * 42 * Returns: 43 * The associated binding channel or an invalid state 44 * (such as its `init` value) if `name` is not registered. 45 */ 46 public Listener!API locate (string name) 47 { 48 synchronized (registryLock) 49 { 50 if (shared(Listener!API)* c = name in this.connections) 51 return *cast(Listener!API*)c; 52 return Listener!API.init; 53 } 54 } 55 56 /** 57 * Register a new name for a connection. 58 * 59 * Associates `name` with `conn` in a process-local map. When the thread 60 * represented by `conn` terminates, any names associated with it will be 61 * automatically unregistered. 62 * 63 * Params: 64 * name = The name to associate with `conn`. 65 * conn = The connection to register. 66 * 67 * Returns: 68 * `true` if the name is available and `conn` is not known to represent a 69 * defunct thread. 70 */ 71 public bool register (string name, Listener!API conn) 72 { 73 synchronized (registryLock) 74 { 75 if (name in this.connections) 76 return false; 77 if (conn.data.isClosed) 78 return false; 79 this.names[conn] ~= name; 80 this.connections[name] = cast(shared)conn; 81 return true; 82 } 83 } 84 85 /** 86 * Removes the registered name associated with a connection. 87 * 88 * Params: 89 * name = The name to unregister. 90 * 91 * Returns: 92 * true if the name is registered, false if not. 93 */ 94 public bool unregister (string name) 95 { 96 import std.algorithm.mutation : remove, SwapStrategy; 97 import std.algorithm.searching : countUntil; 98 99 synchronized (registryLock) 100 { 101 if (shared(Listener!API)* tid = name in this.connections) 102 { 103 auto allNames = *cast(Listener!API*)tid in this.names; 104 auto pos = countUntil(*allNames, name); 105 remove!(SwapStrategy.unstable)(*allNames, pos); 106 this.connections.remove(name); 107 return true; 108 } 109 return false; 110 } 111 } 112 }