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 }