rbczmq 1.7.4 → 1.7.5

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.
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