nanomsg 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/HISTORY CHANGED
@@ -0,0 +1,16 @@
1
+
2
+ = 0.2 /
3
+
4
+ * Adds REQ/REP socket type (ReqSocket, RepSocket).
5
+ * Adds PUB/SUB socket type.
6
+ * Adds SURVEYOR/RESPONDENT socket types. (SurveySocket, RespondSocket)
7
+ * Adds PUSH/PULL socket types. (PushSocket, PullSocket)
8
+ * Adds BUS socket type. (BusSocket)
9
+ * There's an experimental API for devices (.run_loopback, .run_device).
10
+ Devices block a Ruby thread until .terminate is called.
11
+ * Exceptions raised are now subclasses of NanoMsg::Errno
12
+ * Added NanoMsg.terminate, calls nn_term.
13
+
14
+ = 0.1 / 22Aug2013
15
+
16
+ * Initial release, only PAIR sockets supported.
data/README CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  TLDR
3
3
 
4
- An idiomatic nanomsg wrapper for Ruby. (http://nanomsg.org/)
4
+ An idiomatic nanomsg wrapper for Ruby.
5
5
 
6
6
  NANOMSG
7
7
 
@@ -25,6 +25,11 @@ stack. At the moment, nanomsg library supports following transports:
25
25
  * IPC - transport between processes on a single machine
26
26
  * TCP - network transport via TCP
27
27
 
28
+ DOCUMENTATION
29
+
30
+ Check out the examples folder of the project. See http://nanomsg.org/ for more
31
+ documentation.
32
+
28
33
  SYNOPSIS
29
34
 
30
35
  require 'nanomsg'
@@ -37,11 +42,10 @@ SYNOPSIS
37
42
 
38
43
  STATUS
39
44
 
40
- Very early alpha, not much testing has been done. Only the PAIR communication
41
- type works. Nonblocking mode doesn't even begin to work. This will break your
42
- application.
45
+ Very early alpha, not much testing has been done. All socket types and devices
46
+ should work.
43
47
 
44
48
  LICENSE
45
49
 
46
- MIT, 2013, Kaspar Schiess
50
+ See file LICENSE, Copyright (c) 2013 Kaspar Schiess
47
51
 
@@ -0,0 +1,32 @@
1
+ $:.unshift File.dirname(__FILE__) + "/../lib"
2
+ require 'nanomsg'
3
+ include NanoMsg
4
+
5
+ buses = 3.times.map { BusSocket.new }
6
+
7
+ a = 'tcp://127.0.0.1:5432'
8
+ b = 'tcp://127.0.0.1:5433'
9
+
10
+ one, two, three = *buses
11
+
12
+ one.bind a
13
+
14
+ two.bind b
15
+ two.connect a
16
+
17
+ three.connect a
18
+ three.connect b
19
+
20
+ sleep 0.1
21
+
22
+ one.send 'A'
23
+ p two.recv
24
+ p three.recv
25
+
26
+ two.send 'B'
27
+ p one.recv
28
+ p three.recv
29
+
30
+ three.send 'C'
31
+ p one.recv
32
+ p two.recv
@@ -0,0 +1,39 @@
1
+ $:.unshift File.dirname(__FILE__) + "/../lib"
2
+ require 'nanomsg'
3
+ include NanoMsg
4
+
5
+ a = 'tcp://127.0.0.1:5432'
6
+ b = 'tcp://127.0.0.1:5433'
7
+ c = 'tcp://127.0.0.1:5434'
8
+
9
+ one, two, three = 3.times.map { BusSocket.new }
10
+ four, five, six = 3.times.map { BusSocket.new(AF_SP_RAW) }
11
+
12
+ one.bind a
13
+ two.bind b
14
+ three.bind c
15
+
16
+ # one -> four -> four -> two
17
+ four.connect a
18
+ four.connect b
19
+ Thread.start do
20
+ NanoMsg.run_loopback(four)
21
+ end.abort_on_exception = true
22
+
23
+ # one -> five -> six -> three
24
+ five.connect a
25
+ six.connect c
26
+ Thread.start do
27
+ NanoMsg.run_device(five, six)
28
+ end.abort_on_exception = true
29
+
30
+ sleep 0.2
31
+ one.send 'A'
32
+
33
+ # Gets sent to three, which forwards it to two.
34
+ puts two.recv
35
+
36
+ # Gets sent to five, which forwards it through six to three.
37
+ puts three.recv
38
+
39
+ NanoMsg.terminate
@@ -2,10 +2,10 @@
2
2
  $:.unshift File.dirname(__FILE__) + "/../lib"
3
3
  require 'nanomsg'
4
4
 
5
- sock1 = NanoMsg::PairSocket.new(NanoMsg::AF_SP)
5
+ sock1 = NanoMsg::PairSocket.new
6
6
  sock1.bind("ipc:///tmp/test.ipc")
7
7
 
8
- sock2 = NanoMsg::PairSocket.new(NanoMsg::AF_SP)
8
+ sock2 = NanoMsg::PairSocket.new
9
9
  sock2.connect("ipc:///tmp/test.ipc")
10
10
 
11
11
  sock1.send 'test'
@@ -117,8 +117,8 @@ extout_prefix =
117
117
  target_prefix =
118
118
  LOCAL_LIBS =
119
119
  LIBS = $(LIBRUBYARG_SHARED) -lnanomsg -lpthread -ldl -lobjc
120
- SRCS = init.c
121
- OBJS = init.o
120
+ SRCS = constants.c init.c
121
+ OBJS = constants.o init.o
122
122
  TARGET = nanomsg
123
123
  DLLIB = $(TARGET).bundle
124
124
  EXTSTATIC =
@@ -0,0 +1,89 @@
1
+
2
+ #include "ruby/ruby.h"
3
+ #include "ruby/st.h"
4
+
5
+ #include <nanomsg/nn.h>
6
+
7
+ static st_table *syserr_tbl;
8
+
9
+ static VALUE
10
+ errno_initialize(VALUE self)
11
+ {
12
+ VALUE error;
13
+ VALUE klass = rb_obj_class(self);
14
+
15
+ const char *explanation;
16
+ VALUE message;
17
+
18
+ error = rb_const_get(klass, rb_intern("Errno"));
19
+ explanation = nn_strerror(FIX2INT(error));
20
+
21
+ message = rb_str_new(explanation, strlen(explanation));
22
+ rb_call_super(1, &message);
23
+
24
+ rb_iv_set(self, "errno", error);
25
+
26
+ return self;
27
+ }
28
+
29
+ static VALUE
30
+ errno_errno(VALUE self)
31
+ {
32
+ return rb_attr_get(self, rb_intern("errno"));
33
+ }
34
+
35
+ static VALUE
36
+ err_add(VALUE module, int n, const char *name)
37
+ {
38
+ st_data_t error;
39
+
40
+ if (!st_lookup(syserr_tbl, n, &error)) {
41
+ error = rb_define_class_under(module, name, rb_eStandardError);
42
+
43
+ rb_define_const(error, "Errno", INT2NUM(n));
44
+ rb_define_method(error, "initialize", errno_initialize, 0);
45
+ rb_define_method(error, "errno", errno_errno, 0);
46
+
47
+ st_add_direct(syserr_tbl, n, error);
48
+ }
49
+
50
+ return error;
51
+ }
52
+
53
+ VALUE
54
+ errno_lookup(int n)
55
+ {
56
+ st_data_t error;
57
+
58
+ if (!st_lookup(syserr_tbl, n, &error))
59
+ return Qnil;
60
+
61
+ return error;
62
+ }
63
+
64
+ void
65
+ Init_constants(VALUE module)
66
+ {
67
+ VALUE mErrno;
68
+ int i, value;
69
+
70
+ syserr_tbl = st_init_numtable();
71
+
72
+ mErrno = rb_define_module_under(module, "Errno");
73
+
74
+ // Define all constants that nanomsg knows about:
75
+ for (i = 0; ; ++i) {
76
+ const char* name = nn_symbol (i, &value);
77
+ if (name == NULL) break;
78
+
79
+ // I see no point in declaring values other than those starting with NN_:
80
+ if (strncmp(name, "NN_", 3) == 0)
81
+ rb_const_set(module, rb_intern(name), INT2NUM(value));
82
+
83
+ if (strncmp(name, "AF_", 3) == 0)
84
+ rb_const_set(module, rb_intern(name), INT2NUM(value));
85
+
86
+ if (strncmp(name, "E", 1) == 0)
87
+ err_add(mErrno, value, name);
88
+ }
89
+ }
@@ -0,0 +1,9 @@
1
+
2
+ #ifndef CONSTANTS_H
3
+ #define CONSTANTS_H
4
+
5
+ void Init_constants(VALUE module);
6
+ VALUE errno_lookup(int n);
7
+
8
+
9
+ #endif
Binary file
data/ext/init.c CHANGED
@@ -2,11 +2,34 @@
2
2
  #include "ruby/ruby.h"
3
3
 
4
4
  #include <nanomsg/nn.h>
5
+ #include <nanomsg/reqrep.h>
5
6
  #include <nanomsg/pair.h>
7
+ #include <nanomsg/pubsub.h>
8
+ #include <nanomsg/survey.h>
9
+ #include <nanomsg/pipeline.h>
10
+ #include <nanomsg/bus.h>
6
11
 
7
- static VALUE cNanoMsg;
12
+ #include "constants.h"
13
+
14
+ static VALUE mNanoMsg;
8
15
  static VALUE cSocket;
16
+
9
17
  static VALUE cPairSocket;
18
+
19
+ static VALUE cReqSocket;
20
+ static VALUE cRepSocket;
21
+
22
+ static VALUE cPubSocket;
23
+ static VALUE cSubSocket;
24
+
25
+ static VALUE cSurveySocket;
26
+ static VALUE cRespondSocket;
27
+
28
+ static VALUE cPushSocket;
29
+ static VALUE cPullSocket;
30
+
31
+ static VALUE cBusSocket;
32
+
10
33
  static VALUE ceSocketError;
11
34
 
12
35
  struct nmsg_socket {
@@ -65,46 +88,18 @@ sock_alloc(VALUE klass)
65
88
  }
66
89
 
67
90
  static void
68
- sock_raise_error(int code)
91
+ sock_raise_error(int nn_errno)
69
92
  {
70
- printf("socket error %d errno %d\n", code, errno);
71
-
72
- // TODO allow querying of the errno at the very least.
73
- switch (errno) {
74
- case EBADF:
75
- rb_raise(ceSocketError, "The provided socket is invalid.");
76
- break;
77
- case ENOTSUP:
78
- rb_raise(ceSocketError, "The operation is not supported by this socket type.");
79
- break;
80
- case EFSM:
81
- rb_raise(ceSocketError, "The operation cannot be performed on this socket at the moment because the socket is not in the appropriate state.");
82
- break;
83
- case EAGAIN:
84
- rb_raise(ceSocketError, "Non-blocking mode was requested and the message cannot be sent at the moment.");
85
- break;
86
- case EINTR:
87
- rb_raise(ceSocketError, "The operation was interrupted by delivery of a signal before the message was sent.");
88
- break;
89
- case ETIMEDOUT:
90
- rb_raise(ceSocketError, "Individual socket types may define their own specific timeouts. If such timeout is hit, this error will be returned.");
91
- break;
92
- case EAFNOSUPPORT:
93
- rb_raise(ceSocketError, "Specified address family is not supported.");
94
- break;
95
- case EINVAL:
96
- rb_raise(ceSocketError, "Unknown protocol.");
97
- break;
98
- case EMFILE:
99
- rb_raise(ceSocketError, "The limit on the total number of open SP sockets or OS limit for file descriptors has been reached.");
100
- break;
101
- case ETERM:
102
- rb_raise(ceSocketError, "The library is terminating.");
103
- break;
104
- default:
105
- rb_raise(ceSocketError, "Unknown error code %d", errno);
106
- }
93
+ VALUE error = errno_lookup(nn_errno);
94
+
95
+ if (error != Qnil) {
96
+ VALUE exc = rb_class_new_instance(0, NULL, error);
97
+ rb_exc_raise(exc);
98
+ }
99
+
100
+ rb_raise(ceSocketError, "General failure, no such error code %d found.", nn_errno);
107
101
  }
102
+ #define RAISE_SOCK_ERROR { sock_raise_error(nn_errno()); }
108
103
 
109
104
  static VALUE
110
105
  sock_bind(VALUE socket, VALUE bind)
@@ -114,7 +109,7 @@ sock_bind(VALUE socket, VALUE bind)
114
109
 
115
110
  endpoint = nn_bind(sock, StringValueCStr(bind));
116
111
  if (endpoint < 0)
117
- sock_raise_error(endpoint);
112
+ RAISE_SOCK_ERROR;
118
113
 
119
114
  // TODO do something with the endpoint, returning it in a class for example.
120
115
  return Qnil;
@@ -128,14 +123,25 @@ sock_connect(VALUE socket, VALUE connect)
128
123
 
129
124
  endpoint = nn_connect(sock, StringValueCStr(connect));
130
125
  if (endpoint < 0)
131
- sock_raise_error(endpoint);
126
+ RAISE_SOCK_ERROR;
132
127
 
133
128
  // TODO do something with the endpoint, returning it in a class for example.
134
129
  return Qnil;
135
130
  }
136
131
 
132
+ static void
133
+ sock_init(VALUE socket, int domain, int protocol)
134
+ {
135
+ struct nmsg_socket *psock = sock_get_ptr(socket);
136
+
137
+ psock->socket = nn_socket(domain, protocol);
138
+ if (psock->socket < 0)
139
+ RAISE_SOCK_ERROR;
140
+ }
141
+
137
142
  struct ioop {
138
- int last_code;
143
+ int nn_errno;
144
+ int return_code;
139
145
  int sock;
140
146
  char* buffer;
141
147
  long len;
@@ -147,11 +153,18 @@ sock_send_no_gvl(void* data)
147
153
  {
148
154
  struct ioop *pio = data;
149
155
 
150
- while (pio->last_code == EAGAIN && !pio->abort) {
151
- pio->last_code = nn_send(pio->sock, pio->buffer, pio->len, NN_DONTWAIT /* flags */);
156
+ // TODO This is buggy. I cannot make the difference between
157
+ // 'socket gone away' (=EAGAIN) and 'socket busy' (=EAGAIN). So I err on the
158
+ // side of 'socket busy' and do not raise the error. As a consequence, we'll
159
+ // get stuck in an endless loop when the socket is just not answering.
160
+
161
+ while (pio->nn_errno == EAGAIN && !pio->abort) {
162
+ pio->return_code = nn_send(pio->sock, pio->buffer, pio->len, NN_DONTWAIT /* flags */);
152
163
 
153
- if (pio->last_code < 0)
154
- pio->last_code = errno;
164
+ if (pio->return_code < 0)
165
+ pio->nn_errno = nn_errno();
166
+ else
167
+ break;
155
168
  }
156
169
 
157
170
  return Qnil;
@@ -170,7 +183,7 @@ sock_send(VALUE socket, VALUE buffer)
170
183
  struct ioop io;
171
184
 
172
185
  io.sock = sock_get(socket);
173
- io.last_code = EAGAIN;
186
+ io.nn_errno = EAGAIN;
174
187
  io.buffer = StringValuePtr(buffer);
175
188
  io.len = RSTRING_LEN(buffer);
176
189
  io.abort = Qfalse;
@@ -182,10 +195,10 @@ sock_send(VALUE socket, VALUE buffer)
182
195
  if (io.abort)
183
196
  return Qnil;
184
197
 
185
- if (io.last_code < 0)
186
- sock_raise_error(io.last_code);
198
+ if (io.return_code < 0)
199
+ sock_raise_error(io.nn_errno);
187
200
 
188
- return INT2NUM(io.last_code);
201
+ return INT2NUM(io.return_code);
189
202
  }
190
203
 
191
204
  static VALUE
@@ -193,11 +206,18 @@ sock_recv_no_gvl(void* data)
193
206
  {
194
207
  struct ioop *pio = data;
195
208
 
196
- while (pio->last_code == EAGAIN && !pio->abort) {
197
- pio->last_code = nn_recv(pio->sock, &pio->buffer, NN_MSG, NN_DONTWAIT /* flags */);
209
+ // TODO This is buggy. I cannot make the difference between
210
+ // 'socket gone away' (=EAGAIN) and 'socket busy' (=EAGAIN). So I err on the
211
+ // side of 'socket busy' and do not raise the error. As a consequence, we'll
212
+ // get stuck in an endless loop when the socket is just not answering.
213
+
214
+ while (pio->nn_errno == EAGAIN && !pio->abort) {
215
+ pio->return_code = nn_recv(pio->sock, &pio->buffer, NN_MSG, NN_DONTWAIT /* flags */);
198
216
 
199
- if (pio->last_code < 0)
200
- pio->last_code = errno;
217
+ if (pio->return_code < 0)
218
+ pio->nn_errno = nn_errno();
219
+ else
220
+ break;
201
221
  }
202
222
 
203
223
  return Qnil;
@@ -220,52 +240,231 @@ sock_recv(VALUE socket)
220
240
  io.sock = sock_get(socket);
221
241
  io.buffer = (char*) 0;
222
242
  io.abort = Qfalse;
223
- io.last_code = EAGAIN;
243
+ io.nn_errno = EAGAIN;
224
244
 
225
245
  rb_thread_blocking_region(sock_recv_no_gvl, &io, sock_recv_abort, &io);
226
246
 
227
247
  if (io.abort)
228
248
  return Qnil;
229
249
 
230
- if (io.last_code < 0)
231
- sock_raise_error(io.last_code);
250
+ if (io.return_code < 0)
251
+ sock_raise_error(io.nn_errno);
232
252
 
233
- result = rb_str_new(io.buffer, io.last_code);
253
+ result = rb_str_new(io.buffer, io.return_code);
234
254
  nn_freemsg(io.buffer); io.buffer = (char*) 0;
235
255
 
236
256
  return result;
237
257
  }
238
258
 
239
- static VALUE
240
- pair_sock_init(VALUE socket)
259
+ static VALUE
260
+ sock_close_no_gvl(void* data)
241
261
  {
242
- struct nmsg_socket *psock = sock_get_ptr(socket);
262
+ struct ioop *pio = (struct ioop*) data;
243
263
 
244
- psock->socket = nn_socket(AF_SP, NN_PAIR);
245
- if (psock->socket < 0) {
246
- sock_raise_error(psock->socket);
247
- }
264
+ pio->return_code = nn_close(pio->sock);
265
+ if (pio->return_code < 0)
266
+ pio->nn_errno = nn_errno();
248
267
 
249
- return socket;
268
+ return Qnil;
269
+ }
270
+
271
+ static VALUE
272
+ sock_close(VALUE socket)
273
+ {
274
+ struct ioop io;
275
+
276
+ io.sock = sock_get(socket);
277
+
278
+ // I've no idea on how to abort a close (which may block for NN_LINGER
279
+ // seconds), so we'll be uninterruptible.
280
+ rb_thread_blocking_region(sock_close_no_gvl, &io, NULL, NULL);
281
+
282
+ if (io.return_code < 0)
283
+ sock_raise_error(io.nn_errno);
284
+
285
+ return Qnil;
286
+ }
287
+
288
+ #define SOCK_INIT_FUNC(name, type) \
289
+ static VALUE \
290
+ name(int argc, VALUE *argv, VALUE self) \
291
+ { \
292
+ VALUE domain; \
293
+ \
294
+ rb_scan_args(argc, argv, "01", &domain); \
295
+ if (NIL_P(domain)) \
296
+ sock_init(self, AF_SP, (type)); \
297
+ else \
298
+ sock_init(self, FIX2INT(domain), (type)); \
299
+ \
300
+ return self; \
301
+ }
302
+
303
+ SOCK_INIT_FUNC(pair_sock_init, NN_PAIR);
304
+ SOCK_INIT_FUNC(req_sock_init, NN_REQ);
305
+ SOCK_INIT_FUNC(rep_sock_init, NN_REP);
306
+ SOCK_INIT_FUNC(pub_sock_init, NN_PUB);
307
+ SOCK_INIT_FUNC(sub_sock_init, NN_SUB);
308
+ SOCK_INIT_FUNC(srvy_sock_init, NN_SURVEYOR);
309
+ SOCK_INIT_FUNC(resp_sock_init, NN_RESPONDENT);
310
+ SOCK_INIT_FUNC(push_sock_init, NN_PUSH);
311
+ SOCK_INIT_FUNC(pull_sock_init, NN_PULL);
312
+ SOCK_INIT_FUNC(bus_sock_init, NN_BUS);
313
+
314
+ static VALUE
315
+ sub_sock_subscribe(VALUE socket, VALUE channel)
316
+ {
317
+ int sock = sock_get(socket);
318
+ int err;
319
+
320
+ err = nn_setsockopt(
321
+ sock, NN_SUB, NN_SUB_SUBSCRIBE,
322
+ StringValuePtr(channel),
323
+ RSTRING_LEN(channel)
324
+ );
325
+ if (err < 0)
326
+ RAISE_SOCK_ERROR;
327
+
328
+ return socket;
329
+ }
330
+
331
+ static VALUE
332
+ srvy_set_deadline(VALUE self, VALUE deadline)
333
+ {
334
+ int sock = sock_get(self);
335
+ VALUE miliseconds = rb_funcall(deadline, rb_intern("*"), 1, INT2NUM(1000));
336
+ int timeout = FIX2INT(miliseconds);
337
+ int err;
338
+
339
+ err = nn_setsockopt(sock, NN_SURVEYOR, NN_SURVEYOR_DEADLINE, &timeout, sizeof(int));
340
+ if (err < 0)
341
+ RAISE_SOCK_ERROR;
342
+
343
+ return deadline;
344
+ }
345
+
346
+ static VALUE
347
+ srvy_get_deadline(VALUE self)
348
+ {
349
+ int sock = sock_get(self);
350
+ int deadline;
351
+ size_t size = sizeof(int);
352
+
353
+ int err;
354
+
355
+ err = nn_getsockopt(sock, NN_SURVEYOR, NN_SURVEYOR_DEADLINE, &deadline, &size);
356
+ if (err < 0)
357
+ RAISE_SOCK_ERROR;
358
+
359
+ return rb_funcall(INT2NUM(deadline), rb_intern("/"), 1, rb_float_new(1000));
360
+ }
361
+
362
+ static VALUE
363
+ nanomsg_terminate(VALUE self)
364
+ {
365
+ nn_term();
366
+
367
+ return Qnil;
368
+ }
369
+
370
+ struct device_op {
371
+ int sa, sb;
372
+ int err;
373
+ };
374
+
375
+ static VALUE
376
+ nanomsg_run_device_no_gvl(void* data)
377
+ {
378
+ struct device_op *pop = (struct device_op*) data;
379
+
380
+ pop->err = nn_device(pop->sa, pop->sb);
381
+
382
+ return Qnil;
383
+ }
384
+
385
+ static VALUE
386
+ nanomsg_run_device(VALUE self, VALUE a, VALUE b)
387
+ {
388
+ struct device_op dop;
389
+
390
+ dop.sa = sock_get(a);
391
+ dop.sb = sock_get(b);
392
+
393
+ rb_thread_blocking_region(nanomsg_run_device_no_gvl, &dop, NULL, NULL);
394
+ if (dop.err < 0)
395
+ RAISE_SOCK_ERROR;
396
+
397
+ return Qnil;
398
+ }
399
+
400
+ static VALUE
401
+ nanomsg_run_loopback(VALUE self, VALUE a)
402
+ {
403
+ struct device_op dop;
404
+
405
+ dop.sa = sock_get(a);
406
+ dop.sb = -1; // invalid socket, see documentation
407
+
408
+ rb_thread_blocking_region(nanomsg_run_device_no_gvl, &dop, NULL, NULL);
409
+ if (dop.err < 0)
410
+ RAISE_SOCK_ERROR;
411
+
412
+ return Qnil;
250
413
  }
251
414
 
252
415
  void
253
416
  Init_nanomsg(void)
254
417
  {
255
- printf("loading nanomsg extension\n");
418
+ mNanoMsg = rb_define_module("NanoMsg");
419
+ cSocket = rb_define_class_under(mNanoMsg, "Socket", rb_cObject);
420
+ cPairSocket = rb_define_class_under(mNanoMsg, "PairSocket", cSocket);
421
+
422
+ cReqSocket = rb_define_class_under(mNanoMsg, "ReqSocket", cSocket);
423
+ cRepSocket = rb_define_class_under(mNanoMsg, "RepSocket", cSocket);
424
+
425
+ cPubSocket = rb_define_class_under(mNanoMsg, "PubSocket", cSocket);
426
+ cSubSocket = rb_define_class_under(mNanoMsg, "SubSocket", cSocket);
427
+
428
+ cSurveySocket = rb_define_class_under(mNanoMsg, "SurveySocket", cSocket);
429
+ cRespondSocket = rb_define_class_under(mNanoMsg, "RespondSocket", cSocket);
430
+
431
+ cPushSocket = rb_define_class_under(mNanoMsg, "PushSocket", cSocket);
432
+ cPullSocket = rb_define_class_under(mNanoMsg, "PullSocket", cSocket);
433
+
434
+ cBusSocket = rb_define_class_under(mNanoMsg, "BusSocket", cSocket);
256
435
 
257
- cNanoMsg = rb_define_module("NanoMsg");
258
- cSocket = rb_define_class_under(cNanoMsg, "Socket", rb_cObject);
259
- cPairSocket = rb_define_class_under(cNanoMsg, "PairSocket", cSocket);
436
+ ceSocketError = rb_define_class_under(mNanoMsg, "SocketError", rb_eIOError);
260
437
 
261
- ceSocketError = rb_define_class_under(cNanoMsg, "SocketError", rb_eIOError);
438
+ rb_define_singleton_method(mNanoMsg, "terminate", nanomsg_terminate, 0);
439
+ rb_define_singleton_method(mNanoMsg, "run_device", nanomsg_run_device, 2);
440
+ rb_define_singleton_method(mNanoMsg, "run_loopback", nanomsg_run_loopback, 1);
262
441
 
442
+ rb_define_alloc_func(cSocket, sock_alloc);
263
443
  rb_define_method(cSocket, "bind", sock_bind, 1);
264
444
  rb_define_method(cSocket, "connect", sock_connect, 1);
265
445
  rb_define_method(cSocket, "send", sock_send, 1);
266
446
  rb_define_method(cSocket, "recv", sock_recv, 0);
447
+ rb_define_method(cSocket, "close", sock_close, 0);
448
+
449
+ rb_define_method(cPairSocket, "initialize", pair_sock_init, -1);
450
+
451
+ rb_define_method(cReqSocket, "initialize", req_sock_init, -1);
452
+ rb_define_method(cRepSocket, "initialize", rep_sock_init, -1);
453
+
454
+ rb_define_method(cPubSocket, "initialize", pub_sock_init, -1);
455
+ rb_define_method(cSubSocket, "initialize", sub_sock_init, -1);
456
+ rb_define_method(cSubSocket, "subscribe", sub_sock_subscribe, 1);
457
+
458
+ rb_define_method(cSurveySocket, "initialize", srvy_sock_init, -1);
459
+ rb_define_method(cSurveySocket, "deadline=", srvy_set_deadline, 1);
460
+ rb_define_method(cSurveySocket, "deadline", srvy_get_deadline, 0);
461
+ rb_define_method(cRespondSocket, "initialize", resp_sock_init, -1);
462
+
463
+ rb_define_method(cPushSocket, "initialize", push_sock_init, -1);
464
+ rb_define_method(cPullSocket, "initialize", pull_sock_init, -1);
465
+
466
+ rb_define_method(cBusSocket, "initialize", bus_sock_init, -1);
267
467
 
268
- rb_define_alloc_func(cPairSocket, sock_alloc);
269
- rb_define_method(cPairSocket, "initialize", pair_sock_init, 0);
468
+ Init_constants(mNanoMsg);
270
469
  }
271
470
 
data/ext/init.o CHANGED
Binary file
Binary file
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'BUS sockets' do
4
+ def self.examples_for_transport tbind
5
+ name = tbind.split('://').first
6
+
7
+ describe "transport #{name}" do
8
+ describe "using a broker topology" do
9
+ let!(:a) { NanoMsg::BusSocket.new }
10
+ let!(:b) { NanoMsg::BusSocket.new }
11
+
12
+ after(:each) do
13
+ [a, b].each(&:close)
14
+ end
15
+
16
+ it 'forwards messages to everyone (except the producer)' do
17
+ a.bind(tbind)
18
+ b.connect(tbind)
19
+ sleep 0.01
20
+
21
+ a.send 'test'
22
+ b.recv.should == 'test'
23
+
24
+ b.send 'ing'
25
+ a.recv.should == 'ing'
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ examples_for_transport "ipc:///tmp/bus1.ipc"
32
+ examples_for_transport "tcp://127.0.0.1:5558"
33
+ examples_for_transport "inproc://bus1"
34
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ require 'timeout'
4
+
5
+ describe 'Devices' do
6
+ describe "connecting two BUSes" do
7
+ let(:a) { NanoMsg::BusSocket.new(NanoMsg::AF_SP_RAW) }
8
+ let(:b) { NanoMsg::BusSocket.new(NanoMsg::AF_SP_RAW) }
9
+ let(:c) { NanoMsg::BusSocket.new }
10
+ let(:d) { NanoMsg::BusSocket.new }
11
+
12
+ let(:adr1) { 'inproc://a' }
13
+ let(:adr2) { 'inproc://b' }
14
+
15
+ before(:each) do
16
+ a.bind(adr1)
17
+ b.bind(adr2)
18
+
19
+ c.connect adr1
20
+ d.connect adr2
21
+ end
22
+
23
+ let!(:thread) {
24
+ Thread.start do
25
+ begin
26
+ NanoMsg.run_device(a, b)
27
+ rescue NanoMsg::Errno::ETERM
28
+ # Ignore, spec shutdown
29
+ end
30
+ end
31
+ }
32
+
33
+ it "forwards messages" do
34
+ sleep 0.01
35
+ c.send 'test'
36
+ timeout(1) do
37
+ d.recv.should == 'test'
38
+ end
39
+ end
40
+ end
41
+ end
@@ -4,6 +4,11 @@ describe NanoMsg::PairSocket do
4
4
  let(:sock1) { NanoMsg::PairSocket.new }
5
5
  let(:sock2) { NanoMsg::PairSocket.new }
6
6
 
7
+ after(:each) do
8
+ sock1.close
9
+ sock2.close
10
+ end
11
+
7
12
  def self.examples_for_transport tbind
8
13
  name = tbind.split('://').first
9
14
 
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ require 'timeout'
4
+
5
+ describe 'PUB/SUB sockets' do
6
+ describe 'PUB socket' do
7
+ it 'has no #recv method'
8
+ end
9
+ describe 'SUB socket' do
10
+ it 'has no #send method'
11
+ it 'allows subscribing to channels'
12
+ it 'allows unsubscribing to channels'
13
+ end
14
+
15
+ def self.examples_for_transport tbind
16
+ name = tbind.split('://').first
17
+
18
+ describe "transport #{name}" do
19
+ let(:pub) { NanoMsg::PubSocket.new }
20
+ let(:sub1) { NanoMsg::SubSocket.new }
21
+ let(:sub2) { NanoMsg::SubSocket.new }
22
+
23
+ after(:each) {
24
+ pub.close
25
+ sub1.close
26
+ sub2.close
27
+ }
28
+
29
+ around(:each) { |example|
30
+ timeout(1) { example.run }}
31
+
32
+ it 'allows simple pub/sub' do
33
+ pub.bind(tbind)
34
+ sub1.connect(tbind)
35
+ sub2.connect(tbind)
36
+
37
+ sub1.subscribe 'foo'
38
+ sub2.subscribe 'foo'
39
+
40
+ sub1.subscribe 'bar'
41
+ sleep 0.1
42
+
43
+ pub.send 'foo1234'
44
+ sub1.recv.should == 'foo1234'
45
+ sub2.recv.should == 'foo1234'
46
+
47
+ pub.send 'bar4567'
48
+ pub.send 'foo9999'
49
+ sub1.recv.should == 'bar4567'
50
+ sub2.recv.should == 'foo9999'
51
+ end
52
+ end
53
+ end
54
+
55
+ examples_for_transport "ipc:///tmp/pubsub.ipc"
56
+ examples_for_transport "tcp://127.0.0.1:5557"
57
+ examples_for_transport "inproc://pubsub"
58
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'PUSH/PULL pipeline sockets' do
4
+ def self.examples_for_transport tbind
5
+ name = tbind.split('://').first
6
+
7
+ describe "transport #{name}" do
8
+ describe 'fanout' do
9
+ let(:producer) { NanoMsg::PushSocket.new }
10
+ let(:consumer1) { NanoMsg::PullSocket.new }
11
+ let(:consumer2) { NanoMsg::PullSocket.new }
12
+
13
+ after(:each) do
14
+ [producer, consumer2, consumer1].each(&:close)
15
+ end
16
+
17
+ it 'distributes load evenly' do
18
+ producer.bind(tbind)
19
+ consumer1.connect(tbind)
20
+ consumer2.connect(tbind)
21
+ sleep 0.1
22
+
23
+ 100.times do |i|
24
+ producer.send i.to_s
25
+ end
26
+
27
+ n = [0, 0]
28
+ 50.times do
29
+ [consumer1, consumer2].each_with_index do |consumer, idx|
30
+ v = consumer.recv.to_i
31
+ n[idx] += 1
32
+ end
33
+ end
34
+
35
+ # This is about a 5% level of the binominal distribution
36
+ (n.first - n.last).abs.should < 10
37
+ end
38
+ end
39
+ describe 'fanin' do
40
+ let(:producer1) { NanoMsg::PushSocket.new }
41
+ let(:producer2) { NanoMsg::PushSocket.new }
42
+ let(:consumer) { NanoMsg::PullSocket.new }
43
+
44
+ after(:each) do
45
+ [producer1, producer2, consumer].each(&:close)
46
+ end
47
+
48
+ it 'consumes results fairly' do
49
+ consumer.bind(tbind)
50
+ producer1.connect(tbind)
51
+ producer2.connect(tbind)
52
+
53
+ 100.times do |i|
54
+ producer = [producer1, producer2][i%2]
55
+
56
+ producer.send i.to_s
57
+ end
58
+
59
+ 100.times do |i|
60
+ consumer.recv.to_i.should == i
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ examples_for_transport "ipc:///tmp/reqrep.ipc"
68
+ examples_for_transport "tcp://127.0.0.1:5555"
69
+ examples_for_transport "inproc://reqrep"
70
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'REQ/REP sockets' do
4
+ let(:req) { NanoMsg::ReqSocket.new }
5
+ let(:rep) { NanoMsg::RepSocket.new }
6
+
7
+ after(:each) do
8
+ req.close
9
+ rep.close
10
+ end
11
+
12
+ def self.examples_for_transport tbind
13
+ name = tbind.split('://').first
14
+
15
+ describe "transport #{name}" do
16
+ it 'allows simple req/rep' do
17
+ req.bind(tbind)
18
+ rep.connect(tbind)
19
+
20
+ req.send 'req1'
21
+
22
+ rep.recv.should == 'req1'
23
+ rep.send 'rep1'
24
+
25
+ req.recv.should == 'rep1'
26
+ end
27
+ end
28
+ end
29
+
30
+ examples_for_transport "ipc:///tmp/reqrep.ipc"
31
+ examples_for_transport "tcp://127.0.0.1:5555"
32
+ examples_for_transport "inproc://reqrep"
33
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'SURVEY sockets' do
4
+ let(:surveyor) { NanoMsg::SurveySocket.new }
5
+ let(:resp1) { NanoMsg::RespondSocket.new }
6
+ let(:resp2) { NanoMsg::RespondSocket.new }
7
+
8
+ after(:each) do
9
+ [surveyor, resp2, resp1].each(&:close)
10
+ end
11
+
12
+ def self.examples_for_transport tbind
13
+ name = tbind.split('://').first
14
+
15
+ describe "transport #{name}" do
16
+ it 'allows simple surveys' do
17
+ surveyor.deadline.should == 1 # default: 1 second
18
+ surveyor.deadline = 0.1 # 100ms
19
+ surveyor.deadline.should == 0.1
20
+
21
+ surveyor.bind(tbind)
22
+
23
+ resp1.connect(tbind)
24
+ resp2.connect(tbind)
25
+
26
+ sleep 0.1
27
+ surveyor.send 'u there?'
28
+ resp1.recv.should == 'u there?'
29
+ resp2.recv.should == 'u there?'
30
+
31
+ resp1.send 'KTHNXBYE'
32
+ surveyor.recv.should == 'KTHNXBYE'
33
+
34
+ # nanomsg/tests/survey.c says this should be EFSM, the documentation
35
+ # says it should be ETIMEDOUT. Reality wins.
36
+ begin
37
+ surveyor.recv
38
+ rescue NanoMsg::Errno::EFSM
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ examples_for_transport "ipc:///tmp/reqrep.ipc"
45
+ examples_for_transport "tcp://127.0.0.1:5555"
46
+ examples_for_transport "inproc://reqrep"
47
+ end
@@ -0,0 +1,11 @@
1
+
2
+ require 'spec_helper'
3
+
4
+ describe NanoMsg do
5
+ it 'has NN_LINGER constant set to an integer value' do
6
+ NanoMsg::NN_LINGER.should >= 0
7
+ end
8
+ it 'allows shutting down using nn_term' do
9
+ NanoMsg.should respond_to(:terminate)
10
+ end
11
+ end
@@ -1,4 +1,10 @@
1
1
 
2
2
 
3
3
  $:.unshift File.dirname(__FILE__) + "/../ext"
4
- require 'nanomsg'
4
+ require 'nanomsg'
5
+
6
+ RSpec.configure do |rspec|
7
+ rspec.after(:suite) {
8
+ NanoMsg.terminate
9
+ }
10
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nanomsg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,19 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-22 00:00:00.000000000 Z
12
+ date: 2013-08-26 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: ! " nanomsg provides idiomatic Ruby bindings to the C library nanomsg
15
- (nanomsg.org)\n\n nanomsg library is a high-performance implementation of several
14
+ description: ! " nanomsg library is a high-performance implementation of several
16
15
  \"scalability \n protocols\". Scalability protocol's job is to define how multiple
17
- applications \n communicate to form a single distributed application. Implementation
18
- of \n following scalability protocols is available at the moment:\n\n PAIR
19
- - simple one-to-one communication\n BUS - simple many-to-many communication\n
20
- \ REQREP - allows to build clusters of stateless services to process user requests\n
21
- \ PUBSUB - distributes messages to large sets of interested subscribers\n FANIN
22
- - aggregates messages from multiple sources\n FANOUT - load balances messages
23
- among many destinations\n SURVEY - allows to query state of multiple applications
24
- in a single go\n\n WARNING: nanomsg is still in alpha stage!\n"
16
+ applications \n communicate to form a single distributed application. WARNING:
17
+ nanomsg is still in alpha stage!\n"
25
18
  email: kaspar.schiess@absurd.li
26
19
  executables: []
27
20
  extensions:
@@ -34,16 +27,26 @@ files:
34
27
  - README
35
28
  - lib/nanomsg/pair_socket.rb
36
29
  - lib/nanomsg.rb
30
+ - ext/constants.c
31
+ - ext/constants.h
32
+ - ext/constants.o
37
33
  - ext/extconf.rb
38
34
  - ext/init.c
39
35
  - ext/init.o
40
36
  - ext/Makefile
41
- - ext/mkmf.log
42
37
  - ext/nanomsg.bundle
43
- - ext/nanomsg.o
38
+ - examples/bus.rb
39
+ - examples/bus_device.rb
44
40
  - examples/pair.rb
45
41
  - examples/roundtrip_latency.rb
42
+ - spec/lib/nanomsg/bus_spec.rb
43
+ - spec/lib/nanomsg/device_spec.rb
46
44
  - spec/lib/nanomsg/pair_socket_spec.rb
45
+ - spec/lib/nanomsg/pub_sub_spec.rb
46
+ - spec/lib/nanomsg/push_pull_spec.rb
47
+ - spec/lib/nanomsg/req_rep_spec.rb
48
+ - spec/lib/nanomsg/survey_spec.rb
49
+ - spec/lib/nanomsg_spec.rb
47
50
  - spec/spec_helper.rb
48
51
  homepage: https://bitbucket.org/kschiess/nanomsg
49
52
  licenses: []
@@ -1,22 +0,0 @@
1
- have_library: checking for main() in -lnanomsg... -------------------- yes
2
-
3
- "/usr/local/bin/gcc-4.2 -o conftest -I/Users/kschiess/.rvm/rubies/ruby-1.9.3-p392-turbo/include/ruby-1.9.1/x86_64-darwin12.3.0 -I/Users/kschiess/.rvm/rubies/ruby-1.9.3-p392-turbo/include/ruby-1.9.1/ruby/backward -I/Users/kschiess/.rvm/rubies/ruby-1.9.3-p392-turbo/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -I/usr/local/opt/libyaml/include -I/usr/local/opt/readline/include -I/usr/local/opt/libxml2/include -I/usr/local/opt/libxslt/include -I/usr/local/opt/libksba/include -I/usr/local/opt/openssl/include -I/usr/local/opt/sqlite/include -O3 -ggdb -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wshorten-64-to-32 -Wimplicit-function-declaration -fno-common -pipe conftest.c -L. -L/Users/kschiess/.rvm/rubies/ruby-1.9.3-p392-turbo/lib -L/usr/local/opt/libyaml/lib -L/usr/local/opt/readline/lib -L/usr/local/opt/libxml2/lib -L/usr/local/opt/libxslt/lib -L/usr/local/opt/libksba/lib -L/usr/local/opt/openssl/lib -L/usr/local/opt/sqlite/lib -L. -L/usr/local/lib -L/usr/local/opt/libyaml/lib -L/usr/local/opt/readline/lib -L/usr/local/opt/libxml2/lib -L/usr/local/opt/libxslt/lib -L/usr/local/opt/libksba/lib -L/usr/local/opt/openssl/lib -L/usr/local/opt/sqlite/lib -lruby.1.9.1 -lpthread -ldl -lobjc "
4
- checked program was:
5
- /* begin */
6
- 1: #include "ruby.h"
7
- 2:
8
- 3: int main() {return 0;}
9
- /* end */
10
-
11
- "/usr/local/bin/gcc-4.2 -o conftest -I/Users/kschiess/.rvm/rubies/ruby-1.9.3-p392-turbo/include/ruby-1.9.1/x86_64-darwin12.3.0 -I/Users/kschiess/.rvm/rubies/ruby-1.9.3-p392-turbo/include/ruby-1.9.1/ruby/backward -I/Users/kschiess/.rvm/rubies/ruby-1.9.3-p392-turbo/include/ruby-1.9.1 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -I/usr/local/opt/libyaml/include -I/usr/local/opt/readline/include -I/usr/local/opt/libxml2/include -I/usr/local/opt/libxslt/include -I/usr/local/opt/libksba/include -I/usr/local/opt/openssl/include -I/usr/local/opt/sqlite/include -O3 -ggdb -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wshorten-64-to-32 -Wimplicit-function-declaration -fno-common -pipe conftest.c -L. -L/Users/kschiess/.rvm/rubies/ruby-1.9.3-p392-turbo/lib -L/usr/local/opt/libyaml/lib -L/usr/local/opt/readline/lib -L/usr/local/opt/libxml2/lib -L/usr/local/opt/libxslt/lib -L/usr/local/opt/libksba/lib -L/usr/local/opt/openssl/lib -L/usr/local/opt/sqlite/lib -L. -L/usr/local/lib -L/usr/local/opt/libyaml/lib -L/usr/local/opt/readline/lib -L/usr/local/opt/libxml2/lib -L/usr/local/opt/libxslt/lib -L/usr/local/opt/libksba/lib -L/usr/local/opt/openssl/lib -L/usr/local/opt/sqlite/lib -lruby.1.9.1 -lnanomsg -lpthread -ldl -lobjc "
12
- checked program was:
13
- /* begin */
14
- 1: #include "ruby.h"
15
- 2:
16
- 3: /*top*/
17
- 4: int main() {return 0;}
18
- 5: int t() { void ((*volatile p)()); p = (void ((*)()))main; return 0; }
19
- /* end */
20
-
21
- --------------------
22
-
Binary file