@@ -72,13 +72,62 @@ private struct ArgWrapper (T...)
7272 T args;
7373}
7474
75- // / Ditto
76- public final class RemoteAPI (API , Implementation : API ) : API
75+ /* ******************************************************************************
76+
77+ A reference to an alread-instantiated node
78+
79+ This class serves the same purpose as a `RestInterfaceClient`:
80+ it is a client for an already instantiated rest `API` interface.
81+
82+ In order to instantiate a new server (in a remote thread), use the static
83+ `spawn` function.
84+
85+ Params:
86+ API = The interface defining the API to implement
87+
88+ *******************************************************************************/
89+
90+ public final class RemoteAPI (API ) : API
7791{
78- static if (is (typeof (Implementation.__ctor)))
79- private alias CtorParams = Parameters! (Implementation.__ctor);
80- else
81- private alias CtorParams = AliasSeq! ();
92+ /* **************************************************************************
93+
94+ Instantiate a node and start it
95+
96+ This is usually called from the main thread, which will start all the
97+ nodes and then start to process request.
98+ In order to have a connected network, no nodes in any thread should have
99+ a different reference to the same node.
100+ In practice, this means there should only be one `Tid` per "address".
101+
102+ Note:
103+ When the `RemoteAPI` returned by this function is finalized,
104+ the child thread will be shut down.
105+ This ownership mechanism should be replaced with reference counting
106+ in a later version.
107+
108+ Params:
109+ Impl = Type of the implementation to instantiate
110+ args = Arguments to the object's constructor
111+
112+ Returns:
113+ A `RemoteAPI` owning the node reference
114+
115+ ***************************************************************************/
116+
117+ public static RemoteAPI! (API ) spawn (Impl) (CtorParams! Impl args)
118+ {
119+ auto childTid = .spawn(&spawned! (Impl), args);
120+ return new RemoteAPI(childTid, true );
121+ }
122+
123+ // / Helper template to get the constructor's parameters
124+ private static template CtorParams (Impl)
125+ {
126+ static if (is (typeof (Impl.__ctor)))
127+ private alias CtorParams = Parameters! (Impl.__ctor);
128+ else
129+ private alias CtorParams = AliasSeq! ();
130+ }
82131
83132 /* **************************************************************************
84133
@@ -91,11 +140,12 @@ public final class RemoteAPI (API, Implementation : API) : API
91140 `std.concurrency.receive` is not `@safe`, so neither is this.
92141
93142 Params:
143+ Implementation = Type of the implementation to instantiate
94144 args = Arguments to `Implementation`'s constructor
95145
96146 ***************************************************************************/
97147
98- private static void spawned (CtorParams ... ) (CtorParams cargs)
148+ private static void spawned (Implementation ) (CtorParams! Implementation cargs)
99149 {
100150 import std.format ;
101151
@@ -149,45 +199,32 @@ public final class RemoteAPI (API, Implementation : API) : API
149199 // / Whether or not the destructor should destroy the thread
150200 private bool owner;
151201
152- /* **************************************************************************
202+ // Vibe.d mandates that method must be @safe
203+ @safe :
153204
154- Instantiate a node node and start it
205+ /* **************************************************************************
155206
156- This is usually called from the main thread, which will start all the
157- nodes and then start to process request.
158- In order to have a connected network, no nodes in any thread should have
159- a different reference to the same node.
160- In practice, this means there should only be one `Tid` per `Hash`.
207+ Create an instante of a client
161208
162- When this class is finalized, the child thread will be shut down.
209+ This connects to an already instantiated node.
210+ In order to instantiate a node, see the static `spawn` function.
163211
164212 Params:
165- args = Arguments to the object's constructor
213+ tid = `std.concurrency.Tid` of the node.
214+ This can usually be obtained by `std.concurrency.locate`.
166215
167216 ***************************************************************************/
168217
169- public this (CtorParams ... ) (CtorParams args)
218+ public this (Tid tid) @nogc pure nothrow
170219 {
171- this .childTid = spawn(&spawned! (CtorParams), args);
172- this .owner = true ;
220+ this (tid, false );
173221 }
174222
175- // Vibe.d mandates that method must be @safe
176- @safe :
177-
178- /* **************************************************************************
179-
180- Create a reference to an already existing Tid
181-
182- This overload should be used by non-main Threads to get a reference
183- to an already instantiated Node.
184-
185- ***************************************************************************/
186-
187- public this (Tid tid) @nogc pure nothrow
223+ // / Private overload used by `spawn`
224+ private this (Tid tid, bool isOwner) @nogc pure nothrow
188225 {
189226 this .childTid = tid;
190- this .owner = false ;
227+ this .owner = isOwner ;
191228 }
192229
193230 public Tid tid () @nogc pure nothrow
@@ -250,7 +287,7 @@ unittest
250287 { assert (0 ); }
251288 }
252289
253- scope test = new RemoteAPI! ( API , MockAPI) ();
290+ scope test = RemoteAPI! API .spawn ! MockAPI();
254291 assert (test.pubkey() == 42 );
255292}
256293
@@ -295,16 +332,16 @@ unittest
295332 const name = hash.to! string ;
296333 auto tid = std.concurrency.locate (name);
297334 if (tid != tid.init)
298- return new RemoteAPI! ( API , Node) (tid);
335+ return new RemoteAPI! API (tid);
299336
300337 switch (type)
301338 {
302339 case " normal" :
303- auto ret = new RemoteAPI! ( API , Node) (false );
340+ auto ret = RemoteAPI! API .spawn ! Node(false );
304341 std.concurrency.register (name, ret.tid());
305342 return ret;
306343 case " byzantine" :
307- auto ret = new RemoteAPI! ( API , Node) (true );
344+ auto ret = RemoteAPI! API .spawn ! Node(true );
308345 std.concurrency.register (name, ret.tid());
309346 return ret;
310347 default :
@@ -336,3 +373,77 @@ unittest
336373 // Make sure our main thread terminates after everyone else
337374 receiveOnly! int ();
338375}
376+
377+ // / This network have different types of nodes in it
378+ unittest
379+ {
380+ static interface API
381+ {
382+ @safe :
383+ public @property ulong requests ();
384+ public @property ulong value ();
385+ }
386+
387+ static class MasterNode : API
388+ {
389+ @safe :
390+ public override @property ulong requests()
391+ {
392+ return this .requests_;
393+ }
394+
395+ public override @property ulong value()
396+ {
397+ this .requests_++ ;
398+ return 42 ; // Of course
399+ }
400+
401+ private ulong requests_;
402+ }
403+
404+ static class SlaveNode : API
405+ {
406+ @safe :
407+ this (Tid masterTid)
408+ {
409+ this .master = new RemoteAPI! API (masterTid);
410+ }
411+
412+ public override @property ulong requests()
413+ {
414+ return this .requests_;
415+ }
416+
417+ public override @property ulong value()
418+ {
419+ this .requests_++ ;
420+ return master.value();
421+ }
422+
423+ private API master;
424+ private ulong requests_;
425+ }
426+
427+ API [4 ] nodes;
428+ auto master = RemoteAPI! API .spawn! MasterNode();
429+ nodes[0 ] = master;
430+ nodes[1 ] = RemoteAPI! API .spawn! SlaveNode(master.tid());
431+ nodes[2 ] = RemoteAPI! API .spawn! SlaveNode(master.tid());
432+ nodes[3 ] = RemoteAPI! API .spawn! SlaveNode(master.tid());
433+
434+ foreach (n; nodes)
435+ {
436+ assert (n.requests() == 0 );
437+ assert (n.value() == 42 );
438+ }
439+
440+ assert (nodes[0 ].requests() == 4 );
441+
442+ foreach (n; nodes[1 .. $])
443+ {
444+ assert (n.value() == 42 );
445+ assert (n.requests() == 2 );
446+ }
447+
448+ assert (nodes[0 ].requests() == 7 );
449+ }
0 commit comments