@@ -72,13 +72,62 @@ private struct ArgWrapper (T...)
72
72
T args;
73
73
}
74
74
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
77
91
{
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
+ }
82
131
83
132
/* **************************************************************************
84
133
@@ -91,11 +140,12 @@ public final class RemoteAPI (API, Implementation : API) : API
91
140
`std.concurrency.receive` is not `@safe`, so neither is this.
92
141
93
142
Params:
143
+ Implementation = Type of the implementation to instantiate
94
144
args = Arguments to `Implementation`'s constructor
95
145
96
146
***************************************************************************/
97
147
98
- private static void spawned (CtorParams ... ) (CtorParams cargs)
148
+ private static void spawned (Implementation ) (CtorParams! Implementation cargs)
99
149
{
100
150
import std.format ;
101
151
@@ -149,45 +199,32 @@ public final class RemoteAPI (API, Implementation : API) : API
149
199
// / Whether or not the destructor should destroy the thread
150
200
private bool owner;
151
201
152
- /* **************************************************************************
202
+ // Vibe.d mandates that method must be @safe
203
+ @safe :
153
204
154
- Instantiate a node node and start it
205
+ /* **************************************************************************
155
206
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
161
208
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.
163
211
164
212
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`.
166
215
167
216
***************************************************************************/
168
217
169
- public this (CtorParams ... ) (CtorParams args)
218
+ public this (Tid tid) @nogc pure nothrow
170
219
{
171
- this .childTid = spawn(&spawned! (CtorParams), args);
172
- this .owner = true ;
220
+ this (tid, false );
173
221
}
174
222
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
188
225
{
189
226
this .childTid = tid;
190
- this .owner = false ;
227
+ this .owner = isOwner ;
191
228
}
192
229
193
230
public Tid tid () @nogc pure nothrow
@@ -250,7 +287,7 @@ unittest
250
287
{ assert (0 ); }
251
288
}
252
289
253
- scope test = new RemoteAPI! ( API , MockAPI) ();
290
+ scope test = RemoteAPI! API .spawn ! MockAPI();
254
291
assert (test.pubkey() == 42 );
255
292
}
256
293
@@ -295,16 +332,16 @@ unittest
295
332
const name = hash.to! string ;
296
333
auto tid = std.concurrency.locate (name);
297
334
if (tid != tid.init)
298
- return new RemoteAPI! ( API , Node) (tid);
335
+ return new RemoteAPI! API (tid);
299
336
300
337
switch (type)
301
338
{
302
339
case " normal" :
303
- auto ret = new RemoteAPI! ( API , Node) (false );
340
+ auto ret = RemoteAPI! API .spawn ! Node(false );
304
341
std.concurrency.register (name, ret.tid());
305
342
return ret;
306
343
case " byzantine" :
307
- auto ret = new RemoteAPI! ( API , Node) (true );
344
+ auto ret = RemoteAPI! API .spawn ! Node(true );
308
345
std.concurrency.register (name, ret.tid());
309
346
return ret;
310
347
default :
@@ -336,3 +373,77 @@ unittest
336
373
// Make sure our main thread terminates after everyone else
337
374
receiveOnly! int ();
338
375
}
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