nanomsg 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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