rbczmq 1.7.4 → 1.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +5 -13
  2. data/.travis.yml +4 -1
  3. data/CHANGELOG.rdoc +13 -0
  4. data/Gemfile +8 -1
  5. data/Gemfile.lock +211 -2
  6. data/README.rdoc +7 -4
  7. data/ext/rbczmq/beacon.c +6 -3
  8. data/ext/rbczmq/context.c +117 -8
  9. data/ext/rbczmq/context.h +5 -0
  10. data/ext/rbczmq/message.c +6 -4
  11. data/ext/rbczmq/poller.c +12 -5
  12. data/ext/rbczmq/rbczmq_ext.c +2 -0
  13. data/ext/rbczmq/rbczmq_ext.h +1 -0
  14. data/ext/rbczmq/socket.c +135 -59
  15. data/ext/rbczmq/socket.h +2 -0
  16. data/ext/zeromq/CMakeLists.txt +3 -1
  17. data/ext/zeromq/NEWS +28 -3
  18. data/ext/zeromq/doc/zmq.txt +3 -3
  19. data/ext/zeromq/doc/zmq_getsockopt.txt +3 -3
  20. data/ext/zeromq/doc/zmq_msg_get.txt +2 -1
  21. data/ext/zeromq/doc/zmq_msg_more.txt +1 -1
  22. data/ext/zeromq/doc/zmq_setsockopt.txt +18 -19
  23. data/ext/zeromq/doc/zmq_socket.txt +4 -0
  24. data/ext/zeromq/include/zmq.h +14 -18
  25. data/ext/zeromq/src/clock.cpp +57 -2
  26. data/ext/zeromq/src/ctx.cpp +11 -5
  27. data/ext/zeromq/src/devpoll.cpp +5 -0
  28. data/ext/zeromq/src/devpoll.hpp +2 -0
  29. data/ext/zeromq/src/epoll.cpp +5 -0
  30. data/ext/zeromq/src/epoll.hpp +2 -0
  31. data/ext/zeromq/src/i_engine.hpp +2 -2
  32. data/ext/zeromq/src/kqueue.cpp +5 -0
  33. data/ext/zeromq/src/kqueue.hpp +2 -0
  34. data/ext/zeromq/src/pgm_receiver.cpp +2 -2
  35. data/ext/zeromq/src/pgm_receiver.hpp +2 -2
  36. data/ext/zeromq/src/pgm_sender.cpp +2 -2
  37. data/ext/zeromq/src/pgm_sender.hpp +2 -2
  38. data/ext/zeromq/src/poll.cpp +5 -0
  39. data/ext/zeromq/src/poll.hpp +2 -0
  40. data/ext/zeromq/src/router.cpp +2 -0
  41. data/ext/zeromq/src/select.cpp +5 -0
  42. data/ext/zeromq/src/select.hpp +2 -0
  43. data/ext/zeromq/src/session_base.cpp +2 -2
  44. data/ext/zeromq/src/signaler.cpp +73 -99
  45. data/ext/zeromq/src/signaler.hpp +2 -2
  46. data/ext/zeromq/src/socket_base.cpp +42 -40
  47. data/ext/zeromq/src/stream_engine.cpp +60 -58
  48. data/ext/zeromq/src/stream_engine.hpp +7 -8
  49. data/ext/zeromq/src/zmq_utils.cpp +6 -5
  50. data/ext/zeromq/tests/Makefile.am +6 -5
  51. data/ext/zeromq/tests/test_conflate.cpp +2 -5
  52. data/ext/zeromq/tests/test_ctx_destroy.cpp +1 -1
  53. data/ext/zeromq/tests/test_ctx_options.cpp +1 -1
  54. data/ext/zeromq/tests/test_immediate.cpp +1 -2
  55. data/ext/zeromq/tests/test_inproc_connect.cpp +1 -1
  56. data/ext/zeromq/tests/test_iov.cpp +1 -1
  57. data/ext/zeromq/tests/test_many_sockets.cpp +90 -0
  58. data/ext/zeromq/tests/test_monitor.cpp +3 -3
  59. data/ext/zeromq/tests/test_req_relaxed.cpp +1 -1
  60. data/ext/zeromq/tests/test_router_raw_empty.cpp +65 -0
  61. data/ext/zeromq/tests/test_spec_req.cpp +1 -1
  62. data/ext/zeromq/tests/test_stream.cpp +6 -7
  63. data/ext/zeromq/tests/test_sub_forward.cpp +1 -1
  64. data/ext/zeromq/tests/test_term_endpoint.cpp +2 -2
  65. data/ext/zeromq/tests/testutil.hpp +18 -1
  66. data/ext/zeromq/tools/Makefile.am +1 -1
  67. data/lib/zmq/socket.rb +1 -0
  68. data/lib/zmq/socket/stream.rb +44 -0
  69. data/lib/zmq/version.rb +1 -1
  70. data/test/socket/test_dealer_socket.rb +1 -1
  71. data/test/socket/test_pair_socket.rb +1 -1
  72. data/test/socket/test_pair_sockets.rb +1 -1
  73. data/test/socket/test_pub_socket.rb +1 -1
  74. data/test/socket/test_pub_sub_sockets.rb +1 -1
  75. data/test/socket/test_pull_socket.rb +1 -1
  76. data/test/socket/test_push_pull_sockets.rb +1 -1
  77. data/test/socket/test_push_socket.rb +1 -1
  78. data/test/socket/test_rep_socket.rb +1 -1
  79. data/test/socket/test_req_rep_sockets.rb +1 -1
  80. data/test/socket/test_req_socket.rb +1 -1
  81. data/test/socket/test_router_socket.rb +1 -1
  82. data/test/socket/test_routing.rb +1 -1
  83. data/test/socket/test_stream_socket.rb +74 -0
  84. data/test/socket/test_sub_socket.rb +1 -1
  85. data/test/test_beacon.rb +4 -2
  86. data/test/test_context.rb +2 -2
  87. data/test/test_frame.rb +2 -2
  88. data/test/test_handler.rb +2 -2
  89. data/test/test_logger.rb +1 -1
  90. data/test/test_loop.rb +2 -2
  91. data/test/test_message.rb +1 -1
  92. data/test/test_monitoring.rb +15 -3
  93. data/test/test_poller.rb +2 -2
  94. data/test/test_pollitem.rb +2 -2
  95. data/test/test_socket.rb +53 -6
  96. data/test/test_threading.rb +2 -2
  97. data/test/test_timer.rb +2 -2
  98. data/test/test_zmq.rb +2 -2
  99. metadata +109 -104
@@ -1,6 +1,8 @@
1
1
  #ifndef RBCZMQ_CONTEXT_H
2
2
  #define RBCZMQ_CONTEXT_H
3
3
 
4
+ #include "socket.h"
5
+
4
6
  #define ZMQ_CONTEXT_DESTROYED 0x01
5
7
 
6
8
  typedef struct {
@@ -8,6 +10,7 @@ typedef struct {
8
10
  int flags;
9
11
  pid_t pid; /* this is the pid for the process that created the context. Only this process can use the context. */
10
12
  VALUE pidValue; /* this is the key used to ensure one context per process */
13
+ zlist_t* sockets; /* list of socket wrapper objects owned by this context. */
11
14
  } zmq_ctx_wrapper;
12
15
 
13
16
  #define ZmqAssertContext(obj) ZmqAssertType(obj, rb_cZmqContext, "ZMQ::Context")
@@ -32,4 +35,6 @@ VALUE rb_czmq_socket_alloc(VALUE context, zctx_t *ctx, void *s);
32
35
 
33
36
  void _init_rb_czmq_context();
34
37
 
38
+ void rb_czmq_context_destroy_socket(zmq_sock_wrapper* socket);
39
+
35
40
  #endif
@@ -377,7 +377,7 @@ static VALUE rb_czmq_message_pushstr(VALUE obj, VALUE str)
377
377
  ZmqGetMessage(obj);
378
378
  ZmqAssertMessageOwned(message);
379
379
  Check_Type(str, T_STRING);
380
- rc = zmsg_pushmem(message->message, StringValueCStr(str), RSTRING_LEN(str));
380
+ rc = zmsg_pushmem(message->message, RSTRING_PTR(str), RSTRING_LEN(str));
381
381
  ZmqAssert(rc);
382
382
 
383
383
  /* keep zlist of frame ruby objects in sync with message's frame list */
@@ -410,7 +410,7 @@ static VALUE rb_czmq_message_addstr(VALUE obj, VALUE str)
410
410
  ZmqGetMessage(obj);
411
411
  ZmqAssertMessageOwned(message);
412
412
  Check_Type(str, T_STRING);
413
- rc = zmsg_addmem(message->message, StringValueCStr(str), RSTRING_LEN(str));
413
+ rc = zmsg_addmem(message->message, RSTRING_PTR(str), RSTRING_LEN(str));
414
414
  ZmqAssert(rc);
415
415
 
416
416
  /* keep zlist of frame ruby objects in sync with message's frame list */
@@ -603,7 +603,9 @@ static VALUE rb_czmq_message_encode(VALUE obj)
603
603
  ZmqGetMessage(obj);
604
604
  ZmqReturnNilUnlessOwned(message);
605
605
  buff_size = zmsg_encode(message->message, &buff);
606
- return rb_str_new((char *)buff, buff_size);
606
+ VALUE result = rb_str_new((char *)buff, buff_size);
607
+ free(buff);
608
+ return result;
607
609
  }
608
610
 
609
611
  /*
@@ -623,7 +625,7 @@ static VALUE rb_czmq_message_s_decode(ZMQ_UNUSED VALUE obj, VALUE buffer)
623
625
  {
624
626
  zmsg_t * m = NULL;
625
627
  Check_Type(buffer, T_STRING);
626
- m = zmsg_decode((byte *)StringValueCStr(buffer), RSTRING_LEN(buffer));
628
+ m = zmsg_decode((byte *)RSTRING_PTR(buffer), RSTRING_LEN(buffer));
627
629
  if (m == NULL) return Qnil;
628
630
  return rb_czmq_alloc_message(m);
629
631
  }
@@ -154,15 +154,22 @@ VALUE rb_czmq_poller_poll(int argc, VALUE *argv, VALUE obj)
154
154
  args.nitems = poller->poll_size;
155
155
  args.timeout = (long)timeout;
156
156
 
157
+ rb_ary_clear(poller->readables);
158
+ rb_ary_clear(poller->writables);
159
+
157
160
  rc = (int)rb_thread_blocking_region(rb_czmq_nogvl_poll, (void *)&args, RUBY_UBF_IO, 0);
161
+
158
162
  /* only call ZmqAssert if return code is less than zero since zmq_poll returns the number of pollers on success */
159
163
  if (rc < 0) {
160
- ZmqAssert(rc);
164
+ if (zmq_errno() == EINTR || zmq_errno() == EAGAIN) {
165
+ // these are recoverable errors, so return flow to ruby so that retry / interrupt handling can be
166
+ // done in ruby code. Ruby will see a -1 result. If it was an INT (or other) signal, ruby's signal
167
+ // handler will raise the Interrupt exception.
168
+ } else {
169
+ ZmqAssert(rc);
170
+ }
161
171
  }
162
- if (rc == 0) {
163
- rb_ary_clear(poller->readables);
164
- rb_ary_clear(poller->writables);
165
- } else {
172
+ if (rc > 0) {
166
173
  rb_czmq_poller_rebuild_selectables(poller);
167
174
  }
168
175
  return INT2NUM(rc);
@@ -16,6 +16,7 @@ VALUE rb_cZmqReqSocket;
16
16
  VALUE rb_cZmqPairSocket;
17
17
  VALUE rb_cZmqXPubSocket;
18
18
  VALUE rb_cZmqXSubSocket;
19
+ VALUE rb_cZmqStreamSocket;
19
20
 
20
21
  VALUE rb_cZmqFrame;
21
22
  VALUE rb_cZmqMessage;
@@ -249,6 +250,7 @@ void Init_rbczmq_ext()
249
250
  rb_define_const(rb_mZmq, "PULL", INT2NUM(ZMQ_PULL));
250
251
  rb_define_const(rb_mZmq, "XSUB", INT2NUM(ZMQ_XSUB));
251
252
  rb_define_const(rb_mZmq, "XPUB", INT2NUM(ZMQ_XPUB));
253
+ rb_define_const(rb_mZmq, "STREAM", INT2NUM(ZMQ_STREAM));
252
254
 
253
255
  rb_define_const(rb_mZmq, "EFSM", INT2NUM(EFSM));
254
256
  rb_define_const(rb_mZmq, "ENOCOMPATPROTO", INT2NUM(ENOCOMPATPROTO));
@@ -66,6 +66,7 @@ extern VALUE rb_cZmqReqSocket;
66
66
  extern VALUE rb_cZmqPairSocket;
67
67
  extern VALUE rb_cZmqXPubSocket;
68
68
  extern VALUE rb_cZmqXSubSocket;
69
+ extern VALUE rb_cZmqStreamSocket;
69
70
 
70
71
  extern VALUE rb_cZmqFrame;
71
72
  extern VALUE rb_cZmqMessage;
@@ -11,36 +11,6 @@ VALUE intern_on_closed;
11
11
  VALUE intern_on_close_failed;
12
12
  VALUE intern_on_disconnected;
13
13
 
14
- /*
15
- * :nodoc:
16
- * Destroy the socket while the GIL is released - may block depending on socket linger value.
17
- *
18
- */
19
- static VALUE rb_czmq_nogvl_zsocket_destroy(void *ptr)
20
- {
21
- zmq_sock_wrapper *sock = ptr;
22
- errno = 0;
23
- sock->flags |= ZMQ_SOCKET_DESTROYED;
24
- zsocket_destroy(sock->ctx, sock->socket);
25
- return Qnil;
26
- }
27
-
28
- /*
29
- * :nodoc:
30
- * Free all resources for a socket - invoked by the lower level ZMQ::Socket#destroy as well as the GC callback (some
31
- * regressions here still though).
32
- *
33
- */
34
- void rb_czmq_free_sock(zmq_sock_wrapper *sock)
35
- {
36
- if (sock->ctx) {
37
- rb_thread_blocking_region(rb_czmq_nogvl_zsocket_destroy, sock, RUBY_UBF_IO, 0);
38
- if (zmq_errno() == ENOTSOCK) ZmqRaiseSysError();
39
- sock->socket = NULL;
40
- sock->flags |= ZMQ_SOCKET_DESTROYED;
41
- }
42
- }
43
-
44
14
  /*
45
15
  * :nodoc:
46
16
  * GC mark callback
@@ -50,8 +20,9 @@ void rb_czmq_mark_sock(void *ptr)
50
20
  {
51
21
  zmq_sock_wrapper *sock = (zmq_sock_wrapper *)ptr;
52
22
  if (sock && sock->ctx){
53
- if (sock->verbose)
54
- zclock_log ("I: %s socket %p, context %p: GC mark", zsocket_type_str(sock->socket), sock, sock->ctx);
23
+ if (sock->verbose) {
24
+ zclock_log ("I: %s socket %p, context %p: GC mark", sock->flags & ZMQ_SOCKET_DESTROYED ? "(closed)" : zsocket_type_str(sock->socket), sock, sock->ctx);
25
+ }
55
26
  rb_gc_mark(sock->endpoints);
56
27
  rb_gc_mark(sock->thread);
57
28
  rb_gc_mark(sock->context);
@@ -69,14 +40,27 @@ void rb_czmq_mark_sock(void *ptr)
69
40
  void rb_czmq_free_sock_gc(void *ptr)
70
41
  {
71
42
  zmq_sock_wrapper *sock = (zmq_sock_wrapper *)ptr;
72
- if (sock && sock->ctx){
73
- if (sock->verbose)
74
- zclock_log ("I: %s socket %p, context %p: GC free", zsocket_type_str(sock->socket), sock, sock->ctx);
43
+ if (sock) {
44
+ if (sock->verbose) {
45
+ zclock_log ("I: %s socket %p, context %p: GC free", sock->flags & ZMQ_SOCKET_DESTROYED ? "(closed)" : zsocket_type_str(sock->socket), sock, sock ? sock->ctx : NULL);
46
+ }
75
47
  /*
76
48
  XXX: cyclic dependency
77
49
  #4 0x0000000100712524 in zsocket_set_linger (linger=1, socket=<value temporarily unavailable, due to optimizations>) at zsocket.c:288
78
50
  if (sock->socket != NULL && !(sock->flags & ZMQ_SOCKET_DESTROYED)) rb_czmq_free_sock(sock);
51
+
52
+ SOLVED:
53
+
54
+ The above occurs when a socket is not explicitly destroyed and the socket happens to be garbage
55
+ collected *after* the context has been destroyed. When the context is destroyed, all sockets
56
+ in the context are closed internally by czmq. However, the rbczmq ZMQ::Socket object still
57
+ thinks it is alive and will destroy again.
58
+
59
+ The new socket destroy method 'rb_czmq_context_destroy_socket' is idempotent and can safely
60
+ be called after either context or socket has been destroyed.
79
61
  */
62
+
63
+ rb_czmq_context_destroy_socket(sock);
80
64
  xfree(sock);
81
65
  }
82
66
  }
@@ -100,8 +84,8 @@ static VALUE rb_czmq_socket_close(VALUE obj)
100
84
  ZmqSockGuardCrossThread(sock);
101
85
  /* This is useless for production / real use cases as we can't query the state again OR assume
102
86
  anything about the underlying connection. Merely doing the right thing. */
103
- sock->state = ZMQ_SOCKET_PENDING;
104
- rb_czmq_free_sock(sock);
87
+ sock->state = ZMQ_SOCKET_DISCONNECTED;
88
+ rb_czmq_context_destroy_socket(sock);
105
89
  return Qnil;
106
90
  }
107
91
 
@@ -184,7 +168,7 @@ VALUE rb_czmq_nogvl_socket_bind(void *ptr)
184
168
  struct nogvl_conn_args *args = ptr;
185
169
  errno = 0;
186
170
  zmq_sock_wrapper *socket = args->socket;
187
- rc = zsocket_bind(socket->socket, args->endpoint);
171
+ rc = zsocket_bind(socket->socket, "%s", args->endpoint);
188
172
  return (VALUE)rc;
189
173
  }
190
174
 
@@ -199,7 +183,7 @@ VALUE rb_czmq_nogvl_socket_connect(void *ptr)
199
183
  struct nogvl_conn_args *args = ptr;
200
184
  errno = 0;
201
185
  zmq_sock_wrapper *socket = args->socket;
202
- rc = zsocket_connect(socket->socket, args->endpoint);
186
+ rc = zsocket_connect(socket->socket, "%s", args->endpoint);
203
187
  return (VALUE)rc;
204
188
  }
205
189
 
@@ -232,10 +216,14 @@ static VALUE rb_czmq_socket_bind(VALUE obj, VALUE endpoint)
232
216
  if (rc < 0) {
233
217
  ZmqAssert(rc);
234
218
  }
219
+ /* get the endpoint name with any ephemeral ports filled in. */
220
+ char* endpoint_string = zsocket_last_endpoint(sock->socket);
221
+ ZmqAssert(endpoint_string != NULL);
235
222
  if (sock->verbose)
236
- zclock_log ("I: %s socket %p: bound \"%s\"", zsocket_type_str(sock->socket), obj, StringValueCStr(endpoint));
223
+ zclock_log ("I: %s socket %p: bound \"%s\"", zsocket_type_str(sock->socket), obj, endpoint_string);
237
224
  sock->state = ZMQ_SOCKET_BOUND;
238
- rb_ary_push(sock->endpoints, rb_str_new4(endpoint));
225
+ rb_ary_push(sock->endpoints, rb_str_new_cstr(endpoint_string));
226
+ free(endpoint_string);
239
227
  return INT2NUM(rc);
240
228
  }
241
229
 
@@ -266,10 +254,14 @@ static VALUE rb_czmq_socket_connect(VALUE obj, VALUE endpoint)
266
254
  args.endpoint = StringValueCStr(endpoint);
267
255
  rc = (int)rb_thread_blocking_region(rb_czmq_nogvl_socket_connect, (void *)&args, RUBY_UBF_IO, 0);
268
256
  ZmqAssert(rc);
257
+ /* get the endpoint name with any ephemeral ports filled in. */
258
+ char* endpoint_string = zsocket_last_endpoint(sock->socket);
259
+ ZmqAssert(endpoint_string != NULL);
269
260
  if (sock->verbose)
270
- zclock_log ("I: %s socket %p: connected \"%s\"", zsocket_type_str(sock->socket), obj, StringValueCStr(endpoint));
261
+ zclock_log ("I: %s socket %p: connected \"%s\"", zsocket_type_str(sock->socket), obj, endpoint_string);
271
262
  sock->state = ZMQ_SOCKET_CONNECTED;
272
- rb_ary_push(sock->endpoints, rb_str_new4(endpoint));
263
+ rb_ary_push(sock->endpoints, rb_str_new_cstr(endpoint_string));
264
+ free(endpoint_string);
273
265
  return Qtrue;
274
266
  }
275
267
 
@@ -284,7 +276,7 @@ VALUE rb_czmq_nogvl_socket_disconnect(void *ptr)
284
276
  struct nogvl_conn_args *args = ptr;
285
277
  errno = 0;
286
278
  zmq_sock_wrapper *socket = args->socket;
287
- rc = zsocket_disconnect(socket->socket, args->endpoint);
279
+ rc = zsocket_disconnect(socket->socket, "%s", args->endpoint);
288
280
  return (VALUE)rc;
289
281
  }
290
282
 
@@ -318,11 +310,69 @@ static VALUE rb_czmq_socket_disconnect(VALUE obj, VALUE endpoint)
318
310
  ZmqAssert(rc);
319
311
  if (sock->verbose)
320
312
  zclock_log ("I: %s socket %p: disconnected \"%s\"", zsocket_type_str(sock->socket), obj, StringValueCStr(endpoint));
321
- sock->state = ZMQ_SOCKET_DISCONNECTED;
322
313
  rb_ary_delete(sock->endpoints, endpoint);
314
+ long endpoint_count = RARRAY_LEN(sock->endpoints);
315
+ if (endpoint_count == 0) {
316
+ sock->state = ZMQ_SOCKET_DISCONNECTED;
317
+ }
318
+ return Qtrue;
319
+ }
320
+
321
+ /*
322
+ * :nodoc:
323
+ * Unbinds from an endpoint while the GIL is released.
324
+ *
325
+ */
326
+ VALUE rb_czmq_nogvl_socket_unbind(void *ptr)
327
+ {
328
+ int rc;
329
+ struct nogvl_conn_args *args = ptr;
330
+ errno = 0;
331
+ zmq_sock_wrapper *socket = args->socket;
332
+ rc = zsocket_unbind(socket->socket, "%s", args->endpoint);
333
+ return (VALUE)rc;
334
+ }
335
+
336
+
337
+ /*
338
+ * call-seq:
339
+ * sock.disconnect("tcp://localhost:3456") => boolean
340
+ *
341
+ * Attempts to disconnect from a given endpoint.
342
+ *
343
+ * === Examples
344
+ * ctx = ZMQ::Context.new
345
+ * rep = ctx.socket(:REP)
346
+ * port = rep.bind("tcp://localhost:*") => 5432
347
+ * req = ctx.socket(:REQ)
348
+ * req.connect("tcp://localhost:#{port}") => true
349
+ * rep.unbind("tcp://localhost:#{port}") => true
350
+ *
351
+ */
352
+
353
+ static VALUE rb_czmq_socket_unbind(VALUE obj, VALUE endpoint)
354
+ {
355
+ struct nogvl_conn_args args;
356
+ int rc;
357
+ zmq_sock_wrapper *sock = NULL;
358
+ GetZmqSocket(obj);
359
+ ZmqSockGuardCrossThread(sock);
360
+ Check_Type(endpoint, T_STRING);
361
+ args.socket = sock;
362
+ args.endpoint = StringValueCStr(endpoint);
363
+ rc = (int)rb_thread_blocking_region(rb_czmq_nogvl_socket_unbind, (void *)&args, RUBY_UBF_IO, 0);
364
+ ZmqAssert(rc);
365
+ if (sock->verbose)
366
+ zclock_log ("I: %s socket %p: unbound \"%s\"", zsocket_type_str(sock->socket), obj, StringValueCStr(endpoint));
367
+ rb_ary_delete(sock->endpoints, endpoint);
368
+ long endpoint_count = RARRAY_LEN(sock->endpoints);
369
+ if (endpoint_count == 0) {
370
+ sock->state = ZMQ_SOCKET_DISCONNECTED;
371
+ }
323
372
  return Qtrue;
324
373
  }
325
374
 
375
+
326
376
  /*
327
377
  * call-seq:
328
378
  * sock.verbose = true => nil
@@ -537,7 +587,6 @@ static VALUE rb_czmq_socket_recv(VALUE obj)
537
587
 
538
588
  static VALUE rb_czmq_socket_recv_nonblock(VALUE obj)
539
589
  {
540
- char *str = NULL;
541
590
  struct nogvl_recv_args args;
542
591
  errno = 0;
543
592
  VALUE result = Qnil;
@@ -554,12 +603,14 @@ static VALUE rb_czmq_socket_recv_nonblock(VALUE obj)
554
603
  return Qnil;
555
604
  }
556
605
  ZmqAssertSysError();
557
- if (sock->verbose)
558
- zclock_log ("I: %s socket %p: recv \"%s\"", zsocket_type_str(sock->socket), sock->socket, str);
559
606
 
560
607
  result = rb_str_new(zmq_msg_data(&args.message), zmq_msg_size(&args.message));
561
608
  zmq_msg_close(&args.message);
562
609
 
610
+ if (sock->verbose) {
611
+ zclock_log ("I: %s socket %p: recv \"%s\"", zsocket_type_str(sock->socket), sock->socket, StringValueCStr(result));
612
+ }
613
+
563
614
  result = ZmqEncode(result);
564
615
  return result;
565
616
  }
@@ -1664,6 +1715,28 @@ static VALUE rb_czmq_socket_set_opt_sndtimeo(VALUE obj, VALUE value)
1664
1715
  ZmqSetSockOpt(obj, zsocket_set_sndtimeo, "SNDTIMEO", value);
1665
1716
  }
1666
1717
 
1718
+
1719
+ /*
1720
+ * call-seq:
1721
+ * port = sock.bind('tcp://0.0.0.0:*') => 41415
1722
+ * sock.last_endpoint => "tcp://0.0.0.0:41415"
1723
+ *
1724
+ * Gets the last endpoint that this socket connected or bound to.
1725
+ *
1726
+ */
1727
+
1728
+ static VALUE rb_czmq_socket_opt_last_endpoint(VALUE obj)
1729
+ {
1730
+ zmq_sock_wrapper *sock = NULL;
1731
+ GetZmqSocket(obj);
1732
+ char* endpoint_string = zsocket_last_endpoint(sock->socket);
1733
+ VALUE result = rb_str_new_cstr(endpoint_string);
1734
+ if (endpoint_string != NULL) {
1735
+ free(endpoint_string);
1736
+ }
1737
+ return result;
1738
+ }
1739
+
1667
1740
  /*
1668
1741
  * :nodoc:
1669
1742
  * Receives a monitoring event message while the GIL is released.
@@ -1725,7 +1798,7 @@ static VALUE rb_czmq_socket_monitor_thread(void *arg)
1725
1798
  // copy endpoint into ruby string.
1726
1799
  VALUE endpoint_str = rb_str_new(zmq_msg_data(&args.msg_endpoint), zmq_msg_size(&args.msg_endpoint));
1727
1800
  VALUE method = Qnil;
1728
-
1801
+
1729
1802
  switch (event.event) {
1730
1803
  case ZMQ_EVENT_CONNECTED: method = intern_on_connected; break;
1731
1804
  case ZMQ_EVENT_CONNECT_DELAYED: method = intern_on_connect_delayed; break;
@@ -1738,7 +1811,7 @@ static VALUE rb_czmq_socket_monitor_thread(void *arg)
1738
1811
  case ZMQ_EVENT_CLOSED: method = intern_on_closed; break;
1739
1812
  case ZMQ_EVENT_DISCONNECTED: method = intern_on_disconnected; break;
1740
1813
  }
1741
-
1814
+
1742
1815
  if (method != Qnil) {
1743
1816
  rb_funcall(sock->monitor_handler, method, 2, endpoint_str, INT2FIX(event.value));
1744
1817
  }
@@ -1809,16 +1882,17 @@ void _init_rb_czmq_socket()
1809
1882
  {
1810
1883
  rb_cZmqSocket = rb_define_class_under(rb_mZmq, "Socket", rb_cObject);
1811
1884
  rb_cZmqPubSocket = rb_define_class_under(rb_cZmqSocket, "Pub", rb_cZmqSocket);
1812
- rb_cZmqSubSocket = rb_define_class_under(rb_cZmqSocket, "Sub", rb_cZmqSocket);;
1813
- rb_cZmqPushSocket = rb_define_class_under(rb_cZmqSocket, "Push", rb_cZmqSocket);;
1814
- rb_cZmqPullSocket = rb_define_class_under(rb_cZmqSocket, "Pull", rb_cZmqSocket);;
1815
- rb_cZmqRouterSocket = rb_define_class_under(rb_cZmqSocket, "Router", rb_cZmqSocket);;
1816
- rb_cZmqDealerSocket = rb_define_class_under(rb_cZmqSocket, "Dealer", rb_cZmqSocket);;
1817
- rb_cZmqRepSocket = rb_define_class_under(rb_cZmqSocket, "Rep", rb_cZmqSocket);;
1818
- rb_cZmqReqSocket = rb_define_class_under(rb_cZmqSocket, "Req", rb_cZmqSocket);;
1819
- rb_cZmqPairSocket = rb_define_class_under(rb_cZmqSocket, "Pair", rb_cZmqSocket);;
1885
+ rb_cZmqSubSocket = rb_define_class_under(rb_cZmqSocket, "Sub", rb_cZmqSocket);
1886
+ rb_cZmqPushSocket = rb_define_class_under(rb_cZmqSocket, "Push", rb_cZmqSocket);
1887
+ rb_cZmqPullSocket = rb_define_class_under(rb_cZmqSocket, "Pull", rb_cZmqSocket);
1888
+ rb_cZmqRouterSocket = rb_define_class_under(rb_cZmqSocket, "Router", rb_cZmqSocket);
1889
+ rb_cZmqDealerSocket = rb_define_class_under(rb_cZmqSocket, "Dealer", rb_cZmqSocket);
1890
+ rb_cZmqRepSocket = rb_define_class_under(rb_cZmqSocket, "Rep", rb_cZmqSocket);
1891
+ rb_cZmqReqSocket = rb_define_class_under(rb_cZmqSocket, "Req", rb_cZmqSocket);
1892
+ rb_cZmqPairSocket = rb_define_class_under(rb_cZmqSocket, "Pair", rb_cZmqSocket);
1820
1893
  rb_cZmqXPubSocket = rb_define_class_under(rb_cZmqSocket, "XPub", rb_cZmqSocket);
1821
- rb_cZmqXSubSocket = rb_define_class_under(rb_cZmqSocket, "XSub", rb_cZmqSocket);;
1894
+ rb_cZmqXSubSocket = rb_define_class_under(rb_cZmqSocket, "XSub", rb_cZmqSocket);
1895
+ rb_cZmqStreamSocket = rb_define_class_under(rb_cZmqSocket, "Stream", rb_cZmqSocket);
1822
1896
 
1823
1897
  intern_on_connected = rb_intern("on_connected");
1824
1898
  intern_on_connect_delayed = rb_intern("on_connect_delayed");
@@ -1842,6 +1916,7 @@ void _init_rb_czmq_socket()
1842
1916
  rb_define_method(rb_cZmqSocket, "real_bind", rb_czmq_socket_bind, 1);
1843
1917
  rb_define_method(rb_cZmqSocket, "real_connect", rb_czmq_socket_connect, 1);
1844
1918
  rb_define_method(rb_cZmqSocket, "disconnect", rb_czmq_socket_disconnect, 1);
1919
+ rb_define_method(rb_cZmqSocket, "unbind", rb_czmq_socket_unbind, 1);
1845
1920
  rb_define_method(rb_cZmqSocket, "fd", rb_czmq_socket_fd, 0);
1846
1921
  rb_define_alias(rb_cZmqSocket, "to_i", "fd");
1847
1922
  rb_define_method(rb_cZmqSocket, "verbose=", rb_czmq_socket_set_verbose, 1);
@@ -1898,4 +1973,5 @@ void _init_rb_czmq_socket()
1898
1973
  rb_define_method(rb_cZmqSocket, "sndtimeo", rb_czmq_socket_opt_sndtimeo, 0);
1899
1974
  rb_define_method(rb_cZmqSocket, "sndtimeo=", rb_czmq_socket_set_opt_sndtimeo, 1);
1900
1975
  rb_define_method(rb_cZmqSocket, "monitor", rb_czmq_socket_monitor, -1);
1976
+ rb_define_method(rb_cZmqSocket, "last_endpoint", rb_czmq_socket_opt_last_endpoint, 0);
1901
1977
  }
@@ -13,6 +13,7 @@
13
13
  typedef struct {
14
14
  zctx_t *ctx;
15
15
  void *socket;
16
+ void *ctx_wrapper; // zmq_ctx_wrapper - can't be defined yet, circular header includes.
16
17
  int flags;
17
18
  bool verbose;
18
19
  int state;
@@ -153,5 +154,6 @@ extern VALUE intern_on_close_failed;
153
154
  extern VALUE intern_on_disconnected;
154
155
 
155
156
  void _init_rb_czmq_socket();
157
+ VALUE rb_czmq_nogvl_zsocket_destroy(void *ptr);
156
158
 
157
159
  #endif