rbczmq 1.6.4 → 1.7.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.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NTIyYzk0ZmFkOTc4Y2YwMmRlYzc3ZGE0NmQyZTBhOGRlODQyMDcxMA==
5
+ data.tar.gz: !binary |-
6
+ NTI4NzQyMDhkNGRjNGE5NDgxMDE3ZDg5MjU2YTViZWZiMWNjYzQxZA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ ZmQ5NzExNWFhM2ZiODBlZmY4ZDZkMmM5YjBjMDQxODNkZDFlNTUxZmZlMjIz
10
+ N2JkZGU0Mjg4NzJlZTM2ZTEzYjI5ZDdkMGExNDU5MGMzMDE1ZjM2OTUxOTk4
11
+ ZDAyYjcwNTcyOGUzYjNhOTFkYzYyZTM5NTBkNjgyNDgyN2Q0ZDg=
12
+ data.tar.gz: !binary |-
13
+ NDJmOGNmNmMwNGM3YjY5NmUwYTZmOTYzNzAyNGU5YzFmMTc0NGRhYTUzYjIw
14
+ YzI0YjJiODBlMjliMGQyYThjNjFhMWE1MWExZTJhNjAzNTMyNjQzZTM2YWZk
15
+ M2NiYzc5Mzc0NGE0NjlkMmQ1YzBmYTMyYjdiZjkxNmFkMGE5ZTI=
@@ -1,5 +1,12 @@
1
1
  = Changelog
2
2
 
3
+ == 1.7.0 (July 21, 2013)
4
+
5
+ * ZMQ::Frame - Implemented an interal wrapper around the underlying czmq frame objects to better manage memory and object ownership. (Matt Connolly)
6
+ * ZMQ::Frame#gone? and ZMQ::Message#gone? - new methods, return true when the frame/message has been sent and is no longer available for accessing. Methods may return nil for read operations or raise ZMQ::Error if the frame/message would be changed by that method. (Matt Connolly)
7
+ * ZMQ::Message - iterators (#first, #next, #last and #to_a) return consistent frame objects (not duplicates as in previous versions) that can be used with #remove. (Matt Connolly)
8
+ * Sending a message or frame marks it as "gone". The underlying data objects will be released by ZeroMQ as the frames/messages are sent over the socket. (Matt Connolly)
9
+
3
10
  == 1.6.4 (July 10, 2013)
4
11
  * Depend on a checkout task for vendored submodule init (Matt Connolly)
5
12
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rbczmq (1.6.2)
4
+ rbczmq (1.7.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -315,4 +315,4 @@ void _init_rb_czmq_beacon()
315
315
  rb_define_method(rb_cZmqBeacon, "subscribe", rb_czmq_beacon_subscribe, 1);
316
316
  rb_define_method(rb_cZmqBeacon, "unsubscribe", rb_czmq_beacon_unsubscribe, 0);
317
317
  rb_define_method(rb_cZmqBeacon, "pipe", rb_czmq_beacon_pipe, 0);
318
- }
318
+ }
@@ -35,4 +35,4 @@ struct nogvl_beacon_subscribe_args {
35
35
 
36
36
  void _init_rb_czmq_beacon();
37
37
 
38
- #endif
38
+ #endif
@@ -1,18 +1,6 @@
1
1
  #include "rbczmq_ext.h"
2
2
  static VALUE intern_data;
3
3
 
4
- /*
5
- * :nodoc:
6
- * Callback invoked by zframe_destroy in libczmq. We track all frames coerced to native objects in a symbol table
7
- * to guard against a mismatch between allocated frames and the Ruby object space as zframe_destroy is invoked
8
- * throughout libczmq (zmsg.c) where the Ruby GC can't easily track it. Ruby MRI Object finalizers are a real
9
- * pita to deal with.
10
- *
11
- */
12
- void rb_czmq_frame_freed(zframe_t *frame)
13
- {
14
- st_delete(frames_map, (st_data_t*)&frame, 0);
15
- }
16
4
 
17
5
  /*
18
6
  * :nodoc:
@@ -22,8 +10,11 @@ void rb_czmq_frame_freed(zframe_t *frame)
22
10
  VALUE rb_czmq_alloc_frame(zframe_t *frame)
23
11
  {
24
12
  VALUE frame_obj;
25
- ZmqRegisterFrame(frame);
26
- frame_obj = Data_Wrap_Struct(rb_cZmqFrame, 0, rb_czmq_free_frame_gc, frame);
13
+ zmq_frame_wrapper* f;
14
+ frame_obj = Data_Make_Struct(rb_cZmqFrame, zmq_frame_wrapper, 0, rb_czmq_free_frame_gc, f);
15
+ f->frame = frame;
16
+ f->flags = ZMQ_FRAME_OWNED;
17
+ f->message = NULL;
27
18
  rb_obj_call_init(frame_obj, 0, NULL);
28
19
  return frame_obj;
29
20
  }
@@ -35,13 +26,11 @@ VALUE rb_czmq_alloc_frame(zframe_t *frame)
35
26
  * libczmq.
36
27
  *
37
28
  */
38
- void rb_czmq_free_frame(zframe_t *frame)
29
+ void rb_czmq_free_frame(zmq_frame_wrapper *frame)
39
30
  {
40
- if (frame) {
41
- if (st_lookup(frames_map, (st_data_t)frame, 0)) {
42
- st_delete(frames_map, (st_data_t*)&frame, 0);
43
- zframe_destroy(&frame);
44
- }
31
+ if (frame && frame->frame && (frame->flags & ZMQ_FRAME_OWNED) != 0) {
32
+ zframe_destroy(&frame->frame);
33
+ frame->flags &= ~ZMQ_FRAME_OWNED;
45
34
  }
46
35
  }
47
36
 
@@ -52,7 +41,7 @@ void rb_czmq_free_frame(zframe_t *frame)
52
41
  */
53
42
  void rb_czmq_free_frame_gc(void *ptr)
54
43
  {
55
- zframe_t *frame = (zframe_t *)ptr;
44
+ zmq_frame_wrapper *frame = (zmq_frame_wrapper *)ptr;
56
45
  rb_czmq_free_frame(frame);
57
46
  }
58
47
 
@@ -85,10 +74,7 @@ static VALUE rb_czmq_frame_s_new(int argc, VALUE *argv, VALUE frame)
85
74
  ZmqAssertSysError();
86
75
  rb_memerror();
87
76
  }
88
- ZmqRegisterFrame(fr);
89
- frame = Data_Wrap_Struct(rb_cZmqFrame, 0, rb_czmq_free_frame_gc, fr);
90
- rb_obj_call_init(frame, 0, NULL);
91
- return frame;
77
+ return rb_czmq_alloc_frame(fr);
92
78
  }
93
79
 
94
80
  /*
@@ -108,6 +94,7 @@ static VALUE rb_czmq_frame_s_new(int argc, VALUE *argv, VALUE frame)
108
94
  static VALUE rb_czmq_frame_destroy(VALUE obj)
109
95
  {
110
96
  ZmqGetFrame(obj);
97
+ ZmqAssertFrameOwned(frame);
111
98
  rb_czmq_free_frame(frame);
112
99
  return Qnil;
113
100
  }
@@ -128,7 +115,8 @@ static VALUE rb_czmq_frame_size(VALUE obj)
128
115
  {
129
116
  size_t size;
130
117
  ZmqGetFrame(obj);
131
- size = zframe_size(frame);
118
+ ZmqReturnNilUnlessFrameOwned(frame);
119
+ size = zframe_size(frame->frame);
132
120
  return LONG2FIX(size);
133
121
  }
134
122
 
@@ -148,8 +136,9 @@ static VALUE rb_czmq_frame_data(VALUE obj)
148
136
  {
149
137
  size_t size;
150
138
  ZmqGetFrame(obj);
151
- size = zframe_size(frame);
152
- return ZmqEncode(rb_str_new((char *)zframe_data(frame), (long)size));
139
+ ZmqReturnNilUnlessFrameOwned(frame);
140
+ size = zframe_size(frame->frame);
141
+ return ZmqEncode(rb_str_new((char *)zframe_data(frame->frame), (long)size));
153
142
  }
154
143
 
155
144
  /*
@@ -184,7 +173,8 @@ static VALUE rb_czmq_frame_to_s(VALUE obj)
184
173
  static VALUE rb_czmq_frame_strhex(VALUE obj)
185
174
  {
186
175
  ZmqGetFrame(obj);
187
- return rb_str_new2(zframe_strhex(frame));
176
+ ZmqReturnNilUnlessFrameOwned(frame);
177
+ return rb_str_new2(zframe_strhex(frame->frame));
188
178
  }
189
179
 
190
180
  /*
@@ -201,19 +191,15 @@ static VALUE rb_czmq_frame_strhex(VALUE obj)
201
191
 
202
192
  static VALUE rb_czmq_frame_dup(VALUE obj)
203
193
  {
204
- VALUE dup;
205
194
  zframe_t *dup_fr = NULL;
206
- errno = 0;
207
195
  ZmqGetFrame(obj);
208
- dup_fr = zframe_dup(frame);
196
+ ZmqAssertFrameOwned(frame);
197
+ dup_fr = zframe_dup(frame->frame);
209
198
  if (dup_fr == NULL) {
210
199
  ZmqAssertSysError();
211
200
  rb_memerror();
212
201
  }
213
- ZmqRegisterFrame(dup_fr);
214
- dup = Data_Wrap_Struct(rb_cZmqFrame, 0, rb_czmq_free_frame_gc, dup_fr);
215
- rb_obj_call_init(dup, 0, NULL);
216
- return dup;
202
+ return rb_czmq_alloc_frame(dup_fr);
217
203
  }
218
204
 
219
205
  /*
@@ -231,8 +217,9 @@ static VALUE rb_czmq_frame_dup(VALUE obj)
231
217
  static VALUE rb_czmq_frame_data_equals_p(VALUE obj, VALUE data)
232
218
  {
233
219
  ZmqGetFrame(obj);
220
+ ZmqAssertFrameOwned(frame);
234
221
  Check_Type(data, T_STRING);
235
- return (zframe_streq(frame, RSTRING_PTR(data)) == true) ? Qtrue : Qfalse;
222
+ return (zframe_streq(frame->frame, RSTRING_PTR(data)) == true) ? Qtrue : Qfalse;
236
223
  }
237
224
 
238
225
  /*
@@ -250,7 +237,8 @@ static VALUE rb_czmq_frame_data_equals_p(VALUE obj, VALUE data)
250
237
  static VALUE rb_czmq_frame_more_p(VALUE obj)
251
238
  {
252
239
  ZmqGetFrame(obj);
253
- return (zframe_more(frame) == ZFRAME_MORE) ? Qtrue : Qfalse;
240
+ ZmqReturnNilUnlessFrameOwned(frame);
241
+ return (zframe_more(frame->frame) == ZFRAME_MORE) ? Qtrue : Qfalse;
254
242
  }
255
243
 
256
244
  /*
@@ -268,13 +256,14 @@ static VALUE rb_czmq_frame_more_p(VALUE obj)
268
256
 
269
257
  static VALUE rb_czmq_frame_eql_p(VALUE obj, VALUE other_frame)
270
258
  {
271
- zframe_t *other = NULL;
259
+ zmq_frame_wrapper *other = NULL;
272
260
  ZmqGetFrame(obj);
261
+ ZmqAssertFrameOwned(frame);
273
262
  ZmqAssertFrame(other_frame);
274
- Data_Get_Struct(other_frame, zframe_t, other);
275
- if (!other) rb_raise(rb_eTypeError, "uninitialized ZMQ frame!"); \
276
- if (!(st_lookup(frames_map, (st_data_t)other, 0))) rb_raise(rb_eZmqError, "object %p has been destroyed by the ZMQ framework", (void *)other_frame);
277
- return (zframe_eq(frame, other)) ? Qtrue : Qfalse;
263
+ Data_Get_Struct(other_frame, zmq_frame_wrapper, other);
264
+ if (!other || !other->frame) rb_raise(rb_eTypeError, "uninitialized ZMQ frame!"); \
265
+ ZmqAssertFrameOwned(other);
266
+ return (zframe_eq(frame->frame, other->frame)) ? Qtrue : Qfalse;
278
267
  }
279
268
 
280
269
  /*
@@ -313,14 +302,15 @@ static VALUE rb_czmq_frame_equals(VALUE obj, VALUE other_frame)
313
302
  static VALUE rb_czmq_frame_cmp(VALUE obj, VALUE other_frame)
314
303
  {
315
304
  long diff;
316
- zframe_t *other = NULL;
305
+ zmq_frame_wrapper *other = NULL;
317
306
  if (obj == other_frame) return INT2NUM(0);
318
307
  ZmqGetFrame(obj);
308
+ ZmqAssertFrameOwned(frame);
319
309
  ZmqAssertFrame(other_frame);
320
- Data_Get_Struct(other_frame, zframe_t, other);
321
- if (!other) rb_raise(rb_eTypeError, "uninitialized ZMQ frame!"); \
322
- if (!(st_lookup(frames_map, (st_data_t)other, 0))) rb_raise(rb_eZmqError, "object %p has been destroyed by the ZMQ framework", (void *)other_frame);
323
- diff = (zframe_size(frame) - zframe_size(other));
310
+ Data_Get_Struct(other_frame, zmq_frame_wrapper, other);
311
+ if (!other || !other->frame) rb_raise(rb_eTypeError, "uninitialized ZMQ frame!");
312
+ ZmqAssertFrameOwned(frame);
313
+ diff = (zframe_size(frame->frame) - zframe_size(other->frame));
324
314
  if (diff == 0) return INT2NUM(0);
325
315
  if (diff > 0) return INT2NUM(1);
326
316
  return INT2NUM(-1);
@@ -343,6 +333,7 @@ static VALUE rb_czmq_frame_print(int argc, VALUE *argv, VALUE obj)
343
333
  VALUE prefix;
344
334
  const char *print_prefix = NULL;
345
335
  ZmqGetFrame(obj);
336
+ ZmqAssertFrameOwned(frame);
346
337
  rb_scan_args(argc, argv, "01", &prefix);
347
338
  if (NIL_P(prefix)) {
348
339
  print_prefix = "";
@@ -350,7 +341,7 @@ static VALUE rb_czmq_frame_print(int argc, VALUE *argv, VALUE obj)
350
341
  Check_Type(prefix, T_STRING);
351
342
  print_prefix = RSTRING_PTR(prefix);
352
343
  }
353
- zframe_print(frame, (char *)print_prefix);
344
+ zframe_print(frame->frame, (char *)print_prefix);
354
345
  return Qnil;
355
346
  }
356
347
 
@@ -371,12 +362,33 @@ static VALUE rb_czmq_frame_reset(VALUE obj, VALUE data)
371
362
  {
372
363
  errno = 0;
373
364
  ZmqGetFrame(obj);
365
+ ZmqAssertFrameOwned(frame);
374
366
  Check_Type(data, T_STRING);
375
- zframe_reset(frame, (char *)RSTRING_PTR(data), (size_t)RSTRING_LEN(data));
367
+ zframe_reset(frame->frame, (char *)RSTRING_PTR(data), (size_t)RSTRING_LEN(data));
376
368
  ZmqAssertSysError();
377
369
  return Qnil;
378
370
  }
379
371
 
372
+ /*
373
+ * call-seq:
374
+ * frame.gone? #=> false
375
+ * frame.destroy # or send
376
+ * frame.gone? #=> true
377
+ *
378
+ * Return boolean indicating if the ZMQ::Frame is gone (sent or destroyed). If the message is
379
+ * gone, accessor methods will return nil and methods requiring data or methods that mutate
380
+ * the message will raise an exception.
381
+ */
382
+
383
+ static VALUE rb_czmq_frame_gone(VALUE obj)
384
+ {
385
+ ZmqGetFrame(obj);
386
+ return (frame->flags & ZMQ_FRAME_OWNED) ||
387
+ (frame->message != NULL && (frame->message->flags & ZMQ_MESSAGE_OWNED))
388
+ ? Qfalse : Qtrue;
389
+ }
390
+
391
+
380
392
  void _init_rb_czmq_frame()
381
393
  {
382
394
  intern_data = rb_intern("data");
@@ -403,4 +415,5 @@ void _init_rb_czmq_frame()
403
415
  rb_define_method(rb_cZmqFrame, "print", rb_czmq_frame_print, -1);
404
416
  rb_define_alias(rb_cZmqFrame, "dump", "print");
405
417
  rb_define_method(rb_cZmqFrame, "reset", rb_czmq_frame_reset, 1);
418
+ rb_define_method(rb_cZmqFrame, "gone?", rb_czmq_frame_gone, 0);
406
419
  }
@@ -1,21 +1,53 @@
1
1
  #ifndef RBCZMQ_FRAME_H
2
2
  #define RBCZMQ_FRAME_H
3
3
 
4
+ #include "message.h"
5
+
6
+ /* This flag indicates that the wrapped zframe_t is owned by ruby
7
+ and can be freed when the ZMQ::Frame object is garbage collected */
8
+ #define ZMQ_FRAME_OWNED 0x01
9
+
10
+ typedef struct {
11
+ /* The czmq frame object. This is only valid if the frame is owned
12
+ by ruby, or the frame has been added to a message and the message
13
+ is owned by ruby. */
14
+ zframe_t *frame;
15
+
16
+ /* The ruby ZMQ::Message object this frame is attached to, or NULL */
17
+ zmq_message_wrapper* message;
18
+
19
+ int flags;
20
+ } zmq_frame_wrapper;
21
+
4
22
  #define ZmqAssertFrame(obj) ZmqAssertType(obj, rb_cZmqFrame, "ZMQ::Frame")
5
23
  #define ZmqGetFrame(obj) \
6
- zframe_t *frame = NULL; \
24
+ zmq_frame_wrapper *frame = NULL; \
7
25
  ZmqAssertFrame(obj); \
8
- Data_Get_Struct(obj, zframe_t, frame); \
9
- if (!frame) rb_raise(rb_eTypeError, "uninitialized ZMQ frame!"); \
10
- if (!(st_lookup(frames_map, (st_data_t)frame, 0))) rb_raise(rb_eZmqError, "ZMQ::Frame instance %p has been destroyed by the ZMQ framework", (void *)obj);
26
+ Data_Get_Struct(obj, zmq_frame_wrapper, frame); \
27
+ if (!frame) rb_raise(rb_eTypeError, "uninitialized ZMQ frame!");
28
+
29
+ #define ZmqAssertFrameOwned(wrapper) if (!(\
30
+ (wrapper->flags & ZMQ_FRAME_OWNED) != 0 || \
31
+ (wrapper->message != NULL && (wrapper->message->flags & ZMQ_MESSAGE_OWNED) != 0 ) \
32
+ )) { \
33
+ rb_raise(rb_eZmqError, "Cannot access frame that belongs to another message or is gone."); \
34
+ }
35
+
36
+ #define ZmqAssertFrameOwnedNoMessage(wrapper) if (!(\
37
+ (wrapper->flags & ZMQ_FRAME_OWNED) != 0 \
38
+ )) { \
39
+ rb_raise(rb_eZmqError, "Cannot access frame that belongs to another message or is gone."); \
40
+ }
11
41
 
12
- #define ZmqRegisterFrame(fr) \
13
- zframe_freefn((fr), (zframe_free_fn *)rb_czmq_frame_freed, NULL); \
14
- st_insert(frames_map, (st_data_t)(fr), (st_data_t)0);
42
+ #define ZmqReturnNilUnlessFrameOwned(wrapper) if (!(\
43
+ (wrapper->flags & ZMQ_FRAME_OWNED) != 0 || \
44
+ (wrapper->message != NULL && (wrapper->message->flags & ZMQ_MESSAGE_OWNED) != 0 ) \
45
+ )) { \
46
+ return Qnil; \
47
+ }
15
48
 
16
- void rb_czmq_free_frame(zframe_t *frame);
49
+ void rb_czmq_free_frame(zmq_frame_wrapper *frame);
17
50
  void rb_czmq_free_frame_gc(void *ptr);
18
- void rb_czmq_frame_freed(zframe_t *frame);
19
51
 
20
52
  VALUE rb_czmq_alloc_frame(zframe_t *frame);
21
53
 
@@ -8,9 +8,12 @@
8
8
  void rb_czmq_free_message(zmq_message_wrapper *message)
9
9
  {
10
10
  errno = 0;
11
- zmsg_destroy(&message->message);
12
- message->message = NULL;
13
- message->flags |= ZMQ_MESSAGE_DESTROYED;
11
+ if (message != NULL && message->message != NULL && message->flags & ZMQ_MESSAGE_OWNED) {
12
+ zmsg_destroy(&message->message);
13
+ message->message = NULL;
14
+ message->flags &= ~ZMQ_MESSAGE_OWNED;
15
+ zlist_destroy(&message->frames);
16
+ }
14
17
  }
15
18
 
16
19
  /*
@@ -22,11 +25,24 @@ static void rb_czmq_free_message_gc(void *ptr)
22
25
  {
23
26
  zmq_message_wrapper *msg = (zmq_message_wrapper *)ptr;
24
27
  if (msg) {
25
- if (msg->message != NULL && !(msg->flags & ZMQ_MESSAGE_DESTROYED)) rb_czmq_free_message(msg);
28
+ rb_czmq_free_message(msg);
26
29
  xfree(msg);
27
30
  }
28
31
  }
29
32
 
33
+ /*
34
+ * :nodoc:
35
+ * mark the owned ZMQ::Frame objects so that they are not collected.
36
+ */
37
+ void rb_czmq_mark_message(zmq_message_wrapper *message)
38
+ {
39
+ VALUE frame = (VALUE)zlist_first(message->frames);
40
+ while (frame) {
41
+ rb_gc_mark(frame);
42
+ frame = (VALUE)zlist_next(message->frames);
43
+ }
44
+ }
45
+
30
46
  /*
31
47
  * :nodoc:
32
48
  * Coerce a zmsg instance to a native Ruby object.
@@ -40,7 +56,22 @@ VALUE rb_czmq_alloc_message(zmsg_t *message)
40
56
  message_obj = Data_Make_Struct(rb_cZmqMessage, zmq_message_wrapper, 0, rb_czmq_free_message_gc, m);
41
57
  m->message = message;
42
58
  ZmqAssertObjOnAlloc(m->message, m);
43
- m->flags = 0;
59
+ m->flags = ZMQ_MESSAGE_OWNED;
60
+ m->frames = zlist_new();
61
+
62
+ /* create ZMQ::Frame objects for all frames in the message and
63
+ link the ruby objects to this message. */
64
+ zframe_t* zframe = zmsg_first(message);
65
+ while (zframe) {
66
+ VALUE frame_object = rb_czmq_alloc_frame(zframe);
67
+ ZmqGetFrame(frame_object);
68
+ frame->flags &= ~ZMQ_FRAME_OWNED;
69
+ frame->message = m;
70
+ zlist_append(m->frames, (void*)frame_object);
71
+
72
+ zframe = zmsg_next(message);
73
+ }
74
+
44
75
  rb_obj_call_init(message_obj, 0, NULL);
45
76
  return message_obj;
46
77
  }
@@ -58,14 +89,11 @@ VALUE rb_czmq_alloc_message(zmsg_t *message)
58
89
 
59
90
  static VALUE rb_czmq_message_new(VALUE message)
60
91
  {
61
- zmq_message_wrapper *msg = NULL;
62
- errno = 0;
63
- message = Data_Make_Struct(rb_cZmqMessage, zmq_message_wrapper, 0, rb_czmq_free_message_gc, msg);
64
- msg->message = zmsg_new();
65
- ZmqAssertObjOnAlloc(msg->message, msg);
66
- msg->flags = 0;
67
- rb_obj_call_init(message, 0, NULL);
68
- return message;
92
+ zmsg_t* msg = zmsg_new();
93
+ if (msg == NULL) {
94
+ rb_raise(rb_eZmqError, "Failed to allocate message object (zmsg_new).");
95
+ }
96
+ return rb_czmq_alloc_message(msg);
69
97
  }
70
98
 
71
99
  /*
@@ -85,6 +113,7 @@ static VALUE rb_czmq_message_new(VALUE message)
85
113
  static VALUE rb_czmq_message_size(VALUE obj)
86
114
  {
87
115
  ZmqGetMessage(obj);
116
+ ZmqReturnNilUnlessOwned(message);
88
117
  return INT2NUM(zmsg_size(message->message));
89
118
  }
90
119
 
@@ -105,6 +134,7 @@ static VALUE rb_czmq_message_size(VALUE obj)
105
134
  static VALUE rb_czmq_message_content_size(VALUE obj)
106
135
  {
107
136
  ZmqGetMessage(obj);
137
+ ZmqReturnNilUnlessOwned(message);
108
138
  return INT2NUM(zmsg_content_size(message->message));
109
139
  }
110
140
 
@@ -127,10 +157,15 @@ static VALUE rb_czmq_message_push(VALUE obj, VALUE frame_obj)
127
157
  int rc = 0;
128
158
  errno = 0;
129
159
  ZmqGetMessage(obj);
160
+ ZmqAssertMessageOwned(message);
130
161
  ZmqGetFrame(frame_obj);
131
- rc = zmsg_push(message->message, frame);
162
+ ZmqAssertFrameOwnedNoMessage(frame);
163
+
164
+ rc = zmsg_push(message->message, frame->frame);
132
165
  ZmqAssert(rc);
133
- rb_czmq_frame_freed(frame);
166
+ frame->message = message;
167
+ frame->flags &= ~ZMQ_FRAME_OWNED;
168
+ zlist_push(message->frames, (void*)frame_obj);
134
169
  return Qtrue;
135
170
  }
136
171
 
@@ -153,10 +188,15 @@ static VALUE rb_czmq_message_add(VALUE obj, VALUE frame_obj)
153
188
  int rc = 0;
154
189
  errno = 0;
155
190
  ZmqGetMessage(obj);
191
+ ZmqAssertMessageOwned(message);
156
192
  ZmqGetFrame(frame_obj);
157
- rc = zmsg_add(message->message, frame);
193
+ ZmqAssertFrameOwnedNoMessage(frame);
194
+
195
+ rc = zmsg_add(message->message, frame->frame);
158
196
  ZmqAssert(rc);
159
- rb_czmq_frame_freed(frame);
197
+ frame->message = message;
198
+ frame->flags &= ~ZMQ_FRAME_OWNED;
199
+ zlist_append(message->frames, (void*)frame_obj);
160
200
  return Qtrue;
161
201
  }
162
202
 
@@ -179,9 +219,11 @@ static VALUE rb_czmq_message_pop(VALUE obj)
179
219
  {
180
220
  zframe_t *frame = NULL;
181
221
  ZmqGetMessage(obj);
182
- frame = zmsg_pop(message->message);
222
+ ZmqAssertMessageOwned(message);
223
+ frame = zmsg_pop(message->message); /* we now own the frame */
183
224
  if (frame == NULL) return Qnil;
184
- return rb_czmq_alloc_frame(frame);
225
+ VALUE frame_obj = (VALUE)zlist_pop(message->frames);
226
+ return frame_obj ? frame_obj : Qnil;
185
227
  }
186
228
 
187
229
  /*
@@ -200,6 +242,7 @@ static VALUE rb_czmq_message_pop(VALUE obj)
200
242
  static VALUE rb_czmq_message_print(VALUE obj)
201
243
  {
202
244
  ZmqGetMessage(obj);
245
+ ZmqReturnNilUnlessOwned(message);
203
246
  zmsg_dump(message->message);
204
247
  return Qnil;
205
248
  }
@@ -220,11 +263,10 @@ static VALUE rb_czmq_message_print(VALUE obj)
220
263
 
221
264
  static VALUE rb_czmq_message_first(VALUE obj)
222
265
  {
223
- zframe_t *frame = NULL;
224
266
  ZmqGetMessage(obj);
225
- frame = zmsg_first(message->message);
226
- if (frame == NULL) return Qnil;
227
- return rb_czmq_alloc_frame(zframe_dup(frame));
267
+ ZmqReturnNilUnlessOwned(message);
268
+ VALUE frame_obj = (VALUE)zlist_first(message->frames);
269
+ return frame_obj ? frame_obj : Qnil;
228
270
  }
229
271
 
230
272
  /*
@@ -244,11 +286,10 @@ static VALUE rb_czmq_message_first(VALUE obj)
244
286
 
245
287
  static VALUE rb_czmq_message_next(VALUE obj)
246
288
  {
247
- zframe_t *frame = NULL;
248
289
  ZmqGetMessage(obj);
249
- frame = zmsg_next(message->message);
250
- if (frame == NULL) return Qnil;
251
- return rb_czmq_alloc_frame(zframe_dup(frame));
290
+ ZmqReturnNilUnlessOwned(message);
291
+ VALUE frame_obj = (VALUE)zlist_next(message->frames);
292
+ return frame_obj ? frame_obj : Qnil;
252
293
  }
253
294
 
254
295
  /*
@@ -267,11 +308,10 @@ static VALUE rb_czmq_message_next(VALUE obj)
267
308
 
268
309
  static VALUE rb_czmq_message_last(VALUE obj)
269
310
  {
270
- zframe_t *frame = NULL;
271
311
  ZmqGetMessage(obj);
272
- frame = zmsg_last(message->message);
273
- if (frame == NULL) return Qnil;
274
- return rb_czmq_alloc_frame(zframe_dup(frame));
312
+ ZmqReturnNilUnlessOwned(message);
313
+ VALUE frame_obj = (VALUE)zlist_last(message->frames);
314
+ return frame_obj ? frame_obj : Qnil;
275
315
  }
276
316
 
277
317
  /*
@@ -293,10 +333,18 @@ static VALUE rb_czmq_message_last(VALUE obj)
293
333
  static VALUE rb_czmq_message_remove(VALUE obj, VALUE frame_obj)
294
334
  {
295
335
  ZmqGetMessage(obj);
296
- zframe_t *frame = NULL;
297
- ZmqAssertFrame(frame_obj);
298
- Data_Get_Struct(frame_obj, zframe_t, frame);
299
- zmsg_remove(message->message, frame);
336
+ ZmqAssertMessageOwned(message);
337
+ ZmqGetFrame(frame_obj);
338
+
339
+ /* remove from message and our list of frame objects */
340
+ zmsg_remove(message->message, frame->frame);
341
+ zlist_remove(message->frames, (void*)frame_obj);
342
+
343
+ /* removing from message does not destroy the frame,
344
+ therefore, we take ownership of the frame at this point */
345
+ frame->message = NULL;
346
+ frame->flags |= ZMQ_FRAME_OWNED;
347
+
300
348
  return Qnil;
301
349
  }
302
350
 
@@ -317,9 +365,19 @@ static VALUE rb_czmq_message_pushstr(VALUE obj, VALUE str)
317
365
  int rc = 0;
318
366
  errno = 0;
319
367
  ZmqGetMessage(obj);
368
+ ZmqAssertMessageOwned(message);
320
369
  Check_Type(str, T_STRING);
321
370
  rc = zmsg_pushmem(message->message, StringValueCStr(str), RSTRING_LEN(str));
322
371
  ZmqAssert(rc);
372
+
373
+ /* keep zlist of frame ruby objects in sync with message's frame list */
374
+ zframe_t* zframe = zmsg_first(message->message);
375
+ VALUE frame_object = rb_czmq_alloc_frame(zframe);
376
+ ZmqGetFrame(frame_object);
377
+ frame->flags &= ~ZMQ_FRAME_OWNED;
378
+ frame->message = message;
379
+ zlist_push(message->frames, (void*)frame_object);
380
+
323
381
  return Qtrue;
324
382
  }
325
383
 
@@ -340,9 +398,19 @@ static VALUE rb_czmq_message_addstr(VALUE obj, VALUE str)
340
398
  int rc = 0;
341
399
  errno = 0;
342
400
  ZmqGetMessage(obj);
401
+ ZmqAssertMessageOwned(message);
343
402
  Check_Type(str, T_STRING);
344
403
  rc = zmsg_addmem(message->message, StringValueCStr(str), RSTRING_LEN(str));
345
404
  ZmqAssert(rc);
405
+
406
+ /* keep zlist of frame ruby objects in sync with message's frame list */
407
+ zframe_t* zframe = zmsg_last(message->message);
408
+ VALUE frame_object = rb_czmq_alloc_frame(zframe);
409
+ ZmqGetFrame(frame_object);
410
+ frame->flags &= ~ZMQ_FRAME_OWNED;
411
+ frame->message = message;
412
+ zlist_append(message->frames, (void*)frame_object);
413
+
346
414
  return Qtrue;
347
415
  }
348
416
 
@@ -363,8 +431,13 @@ static VALUE rb_czmq_message_popstr(VALUE obj)
363
431
  {
364
432
  char *str = NULL;
365
433
  ZmqGetMessage(obj);
434
+ ZmqAssertMessageOwned(message);
366
435
  str = zmsg_popstr(message->message);
367
436
  if (str == NULL) return Qnil;
437
+
438
+ /* destroys the frame, keep frame objects list in sync: */
439
+ zlist_pop(message->frames);
440
+
368
441
  return rb_str_new2(str);
369
442
  }
370
443
 
@@ -386,9 +459,30 @@ static VALUE rb_czmq_message_wrap(VALUE obj, VALUE frame_obj)
386
459
  {
387
460
  errno = 0;
388
461
  ZmqGetMessage(obj);
389
- ZmqGetFrame(frame_obj);
390
- zmsg_wrap(message->message, frame);
391
- rb_czmq_frame_freed(frame);
462
+ ZmqAssertMessageOwned(message);
463
+
464
+ {
465
+ ZmqGetFrame(frame_obj);
466
+ ZmqAssertFrameOwned(frame);
467
+ zmsg_wrap(message->message, frame->frame);
468
+ frame->flags &= ~ZMQ_FRAME_OWNED;
469
+ frame->message = message;
470
+ }
471
+
472
+ /* keep frame objects list in sync. Two frames have been added. frame_obj from above
473
+ and a new one for the empty frame. */
474
+ {
475
+ zmsg_first(message->message);
476
+ zframe_t* empty_frame = zmsg_next(message->message);
477
+
478
+ VALUE empty_frame_object = rb_czmq_alloc_frame(empty_frame);
479
+ ZmqGetFrame(empty_frame_object);
480
+ frame->flags &= ~ZMQ_FRAME_OWNED;
481
+ frame->message = message;
482
+
483
+ zlist_push(message->frames, (void*)empty_frame_object);
484
+ zlist_push(message->frames, (void*)frame_obj);
485
+ }
392
486
  return Qnil;
393
487
  }
394
488
 
@@ -410,11 +504,29 @@ static VALUE rb_czmq_message_wrap(VALUE obj, VALUE frame_obj)
410
504
 
411
505
  static VALUE rb_czmq_message_unwrap(VALUE obj)
412
506
  {
413
- zframe_t *frame = NULL;
414
507
  ZmqGetMessage(obj);
415
- frame = zmsg_unwrap(message->message);
416
- if (frame == NULL) return Qnil;
417
- return rb_czmq_alloc_frame(frame);
508
+ ZmqAssertMessageOwned(message);
509
+
510
+ /* reimplemented the zmsg_unwrap function for simpler logic: */
511
+ zframe_t *zframe = zmsg_pop(message->message);
512
+ VALUE frame_obj = 0;
513
+ if (zframe != NULL) {
514
+ frame_obj = (VALUE)zlist_pop(message->frames);
515
+ }
516
+
517
+ zframe_t *empty = zmsg_first(message->message);
518
+ if (zframe_size(empty) == 0) {
519
+ empty = zmsg_pop(message->message);
520
+ zframe_destroy (&empty);
521
+ zlist_pop(message->frames);
522
+ }
523
+
524
+ {
525
+ ZmqGetFrame(frame_obj);
526
+ frame->message = NULL;
527
+ frame->flags |= ZMQ_FRAME_OWNED;
528
+ }
529
+ return frame_obj ? frame_obj : Qnil;
418
530
  }
419
531
 
420
532
  /*
@@ -431,16 +543,11 @@ static VALUE rb_czmq_message_unwrap(VALUE obj)
431
543
 
432
544
  static VALUE rb_czmq_message_dup(VALUE obj)
433
545
  {
434
- VALUE dup;
435
- zmq_message_wrapper *dup_msg = NULL;
436
- errno = 0;
437
546
  ZmqGetMessage(obj);
438
- dup = Data_Make_Struct(rb_cZmqMessage, zmq_message_wrapper, 0, rb_czmq_free_message_gc, dup_msg);
439
- dup_msg->message = zmsg_dup(message->message);
440
- ZmqAssertObjOnAlloc(dup_msg->message, dup_msg);
441
- dup_msg->flags = message->flags;
442
- rb_obj_call_init(dup, 0, NULL);
443
- return dup;
547
+ ZmqAssertMessageOwned(message);
548
+
549
+ zmsg_t* dup_msg = zmsg_dup(message->message);
550
+ return rb_czmq_alloc_message(dup_msg);
444
551
  }
445
552
 
446
553
  /*
@@ -460,6 +567,7 @@ static VALUE rb_czmq_message_dup(VALUE obj)
460
567
  static VALUE rb_czmq_message_destroy(VALUE obj)
461
568
  {
462
569
  ZmqGetMessage(obj);
570
+ ZmqAssertMessageOwned(message);
463
571
  rb_czmq_free_message(message);
464
572
  return Qnil;
465
573
  }
@@ -483,6 +591,7 @@ static VALUE rb_czmq_message_encode(VALUE obj)
483
591
  byte *buff;
484
592
  size_t buff_size;
485
593
  ZmqGetMessage(obj);
594
+ ZmqReturnNilUnlessOwned(message);
486
595
  buff_size = zmsg_encode(message->message, &buff);
487
596
  return rb_str_new((char *)buff, buff_size);
488
597
  }
@@ -531,8 +640,10 @@ static VALUE rb_czmq_message_eql_p(VALUE obj, VALUE other_message)
531
640
  size_t other_buff_size;
532
641
  ZmqGetMessage(obj);
533
642
  ZmqAssertMessage(other_message);
643
+ ZmqAssertMessageOwned(message);
534
644
  Data_Get_Struct(other_message, zmq_message_wrapper, other);
535
645
  if (!other) rb_raise(rb_eTypeError, "uninitialized ZMQ message!");
646
+ ZmqAssertMessageOwned(other);
536
647
 
537
648
  if (zmsg_size(message->message) != zmsg_size(other->message)) return Qfalse;
538
649
  if (zmsg_content_size(message->message) != zmsg_content_size(other->message)) return Qfalse;
@@ -580,15 +691,33 @@ static VALUE rb_czmq_message_to_a(VALUE obj)
580
691
  {
581
692
  VALUE ary;
582
693
  ZmqGetMessage(obj);
583
- ary = rb_ary_new2(zmsg_size(message->message));
584
- zframe_t *frame = zmsg_first(message->message);
585
- while (frame) {
586
- rb_ary_push(ary, rb_czmq_alloc_frame(zframe_dup(frame)));
587
- frame = zmsg_next(message->message);
694
+ ZmqAssertMessageOwned(message);
695
+ ary = rb_ary_new2(zlist_size(message->frames));
696
+ VALUE frame_obj = (VALUE)zlist_first(message->frames);
697
+ while (frame_obj) {
698
+ rb_ary_push(ary, frame_obj);
699
+ frame_obj = (VALUE)zlist_next(message->frames);
588
700
  }
589
701
  return ary;
590
702
  }
591
703
 
704
+ /*
705
+ * call-seq:
706
+ * msg.gone? #=> false
707
+ * msg.destroy # or send
708
+ * msg.gone? #=> true
709
+ *
710
+ * Return boolean indicating if the ZMQ::Message is gone (sent or destroyed). If the message is
711
+ * gone, accessor methods will return nil and methods requiring data or methods that mutate
712
+ * the message will raise an exception.
713
+ */
714
+
715
+ static VALUE rb_czmq_message_gone(VALUE obj)
716
+ {
717
+ ZmqGetMessage(obj);
718
+ return (message->flags & ZMQ_MESSAGE_OWNED) ? Qfalse : Qtrue;
719
+ }
720
+
592
721
  void _init_rb_czmq_message()
593
722
  {
594
723
  rb_cZmqMessage = rb_define_class_under(rb_mZmq, "Message", rb_cObject);
@@ -617,4 +746,5 @@ void _init_rb_czmq_message()
617
746
  rb_define_method(rb_cZmqMessage, "eql?", rb_czmq_message_equals, 1);
618
747
  rb_define_method(rb_cZmqMessage, "==", rb_czmq_message_equals, 1);
619
748
  rb_define_method(rb_cZmqMessage, "to_a", rb_czmq_message_to_a, 0);
749
+ rb_define_method(rb_cZmqMessage, "gone?", rb_czmq_message_gone, 0);
620
750
  }
@@ -1,11 +1,15 @@
1
1
  #ifndef RBCZMQ_MESSAGE_H
2
2
  #define RBCZMQ_MESSAGE_H
3
3
 
4
- #define ZMQ_MESSAGE_DESTROYED 0x01
4
+ /* This flag indicates that the wrapped zmsg_t is owned by ruby
5
+ and can be freed when the ZMQ::Message object is garbage collected */
6
+ #define ZMQ_MESSAGE_OWNED 0x01
5
7
 
6
8
  typedef struct {
7
9
  zmsg_t *message;
8
10
  int flags;
11
+ /* a zlist of frame wrapper objects for the frames in this message */
12
+ zlist_t *frames;
9
13
  } zmq_message_wrapper;
10
14
 
11
15
  #define ZmqAssertMessage(obj) ZmqAssertType(obj, rb_cZmqMessage, "ZMQ::Message")
@@ -13,11 +17,23 @@ typedef struct {
13
17
  zmq_message_wrapper *message = NULL; \
14
18
  ZmqAssertMessage(obj); \
15
19
  Data_Get_Struct(obj, zmq_message_wrapper, message); \
16
- if (!message) rb_raise(rb_eTypeError, "uninitialized ZMQ message!"); \
20
+ if (!message) rb_raise(rb_eTypeError, "uninitialized ZMQ message!");
21
+
22
+ /*
17
23
  if (message->flags & ZMQ_MESSAGE_DESTROYED) rb_raise(rb_eZmqError, "ZMQ::Message instance %p has been destroyed by the ZMQ framework", (void *)obj);
24
+ */
25
+
26
+ #define ZmqReturnNilUnlessOwned(wrapper) if (wrapper && (wrapper->flags & ZMQ_MESSAGE_OWNED) == 0) { \
27
+ return Qnil; \
28
+ }
29
+
30
+ #define ZmqAssertMessageOwned(wrapper) if (wrapper && (wrapper->flags & ZMQ_MESSAGE_OWNED) == 0) { \
31
+ rb_raise(rb_eTypeError, "Cannot modify a message that is gone."); \
32
+ }
18
33
 
19
34
  VALUE rb_czmq_alloc_message(zmsg_t *message);
20
35
  void rb_czmq_free_message(zmq_message_wrapper *message);
36
+ void rb_czmq_mark_message(zmq_message_wrapper *message);
21
37
 
22
38
  void _init_rb_czmq_message();
23
39
 
@@ -250,4 +250,4 @@ void _init_rb_czmq_pollitem()
250
250
  rb_define_method(rb_cZmqPollitem, "handler", rb_czmq_pollitem_handler, 0);
251
251
  rb_define_method(rb_cZmqPollitem, "handler=", rb_czmq_pollitem_handler_equals, 1);
252
252
  rb_define_method(rb_cZmqPollitem, "verbose=", rb_czmq_pollitem_set_verbose, 1);
253
- }
253
+ }
@@ -25,8 +25,6 @@ VALUE rb_cZmqPoller;
25
25
  VALUE rb_cZmqPollitem;
26
26
  VALUE rb_cZmqBeacon;
27
27
 
28
- st_table *frames_map = NULL;
29
-
30
28
  VALUE intern_call;
31
29
  VALUE intern_readable;
32
30
  VALUE intern_writable;
@@ -216,8 +214,6 @@ static VALUE rb_czmq_m_proxy(int argc, VALUE *argv, ZMQ_UNUSED VALUE klass)
216
214
 
217
215
  void Init_rbczmq_ext()
218
216
  {
219
- frames_map = st_init_numtable();
220
-
221
217
  intern_call = rb_intern("call");
222
218
  intern_readable = rb_intern("on_readable");
223
219
  intern_writable = rb_intern("on_writable");
@@ -75,8 +75,6 @@ extern VALUE rb_cZmqPoller;
75
75
  extern VALUE rb_cZmqPollitem;
76
76
  extern VALUE rb_cZmqBeacon;
77
77
 
78
- extern st_table *frames_map;
79
-
80
78
  extern VALUE intern_call;
81
79
  extern VALUE intern_readable;
82
80
  extern VALUE intern_writable;
@@ -229,7 +229,7 @@ static VALUE rb_czmq_socket_bind(VALUE obj, VALUE endpoint)
229
229
  args.endpoint = StringValueCStr(endpoint);
230
230
  rc = (int)rb_thread_blocking_region(rb_czmq_nogvl_socket_bind, (void *)&args, RUBY_UBF_IO, 0);
231
231
  /* ZmqAssert will return false on any non-zero return code. Bind returns the port number */
232
- if (rc < 0) {
232
+ if (rc < 0) {
233
233
  ZmqAssert(rc);
234
234
  }
235
235
  if (sock->verbose)
@@ -562,6 +562,7 @@ static VALUE rb_czmq_socket_send_frame(int argc, VALUE *argv, VALUE obj)
562
562
  ZmqSockGuardCrossThread(sock);
563
563
  rb_scan_args(argc, argv, "11", &frame_obj, &flags);
564
564
  ZmqGetFrame(frame_obj);
565
+ ZmqAssertFrameOwnedNoMessage(frame);
565
566
 
566
567
  if (NIL_P(flags)) {
567
568
  flgs = 0;
@@ -573,13 +574,17 @@ static VALUE rb_czmq_socket_send_frame(int argc, VALUE *argv, VALUE obj)
573
574
 
574
575
  if (sock->verbose) {
575
576
  cur_time = rb_czmq_formatted_current_time();
576
- print_frame = (flgs & ZFRAME_REUSE) ? frame : zframe_dup(frame);
577
+ print_frame = (flgs & ZFRAME_REUSE) ? frame->frame : zframe_dup(frame->frame);
577
578
  }
578
579
  args.socket = sock;
579
- args.frame = frame;
580
+ args.frame = frame->frame;
580
581
  args.flags = flgs;
581
582
  rc = (int)rb_thread_blocking_region(rb_czmq_nogvl_send_frame, (void *)&args, RUBY_UBF_IO, 0);
582
583
  ZmqAssert(rc);
584
+ if ((flgs & ZFRAME_REUSE) == 0) {
585
+ /* frame has been destroyed, clear the owns flag */
586
+ frame->flags &= ~ZMQ_FRAME_OWNED;
587
+ }
583
588
  if (sock->verbose) ZmqDumpFrame("send_frame", print_frame);
584
589
  return Qtrue;
585
590
  }
@@ -623,11 +628,12 @@ static VALUE rb_czmq_socket_send_message(VALUE obj, VALUE message_obj)
623
628
  ZmqAssertSocketNotPending(sock, "can only send on a bound or connected socket!");
624
629
  ZmqSockGuardCrossThread(sock);
625
630
  ZmqGetMessage(message_obj);
631
+ ZmqAssertMessageOwned(message);
626
632
  if (sock->verbose) print_message = zmsg_dup(message->message);
627
633
  args.socket = sock;
628
634
  args.message = message->message;
629
635
  rb_thread_blocking_region(rb_czmq_nogvl_send_message, (void *)&args, RUBY_UBF_IO, 0);
630
- message->flags |= ZMQ_MESSAGE_DESTROYED;
636
+ message->flags &= ~ZMQ_MESSAGE_OWNED;
631
637
  if (sock->verbose) ZmqDumpMessage("send_message", print_message);
632
638
  return Qnil;
633
639
  }
@@ -1652,7 +1658,7 @@ static VALUE rb_czmq_socket_monitor_thread(void *arg)
1652
1658
 
1653
1659
  args.monitored_socket_wrapper = sock;
1654
1660
  args.socket = s;
1655
-
1661
+
1656
1662
  while (1) {
1657
1663
  zmq_msg_init (&args.msg);
1658
1664
  rc = (int)rb_thread_blocking_region(rb_czmq_nogvl_monitor_recv, (void *)&args, RUBY_UBF_IO, 0);
data/lib/zmq.rb CHANGED
@@ -82,4 +82,5 @@ require "zmq/timer"
82
82
  require "zmq/frame"
83
83
  require "zmq/message"
84
84
  require "zmq/poller"
85
- require "zmq/pollitem"
85
+ require "zmq/pollitem"
86
+ require "zmq/logger"
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ require 'logger'
4
+
5
+ module ZMQ
6
+
7
+ # A logging class that sends its output to a ZMQ socket. This allows log messages to be sent
8
+ # asynchronously to another process and across a network if required. The receiving socket
9
+ # will receive messages, each with text of one log message.
10
+ #
11
+ # Example usage:
12
+ #
13
+ # socket = context.socket(ZMQ::PUSH)
14
+ # socket.connect("tcp://logserver:5555")
15
+ # logger = ZMQ::Logger.new(socket)
16
+ # logger.debug("Hello logger")
17
+ #
18
+ class Logger < ::Logger
19
+
20
+ class InvalidSocketError < ZMQ::Error ; end
21
+
22
+ # only these socket classes are allowed to be used for sending
23
+ VALID_SOCKET_CLASSES = [
24
+ Socket::Pub,
25
+ Socket::Push,
26
+ Socket::Pair
27
+ ]
28
+
29
+ # Initialise a new logger object. The logger sends log messages to a socket.
30
+ # The caller is responsible for connecting the socket before using the logger to
31
+ # send log output to the socket.
32
+ #
33
+ # Supported socket types are a Socket::Pub, Socket::Push, and Socket::Pair
34
+ def initialize(socket)
35
+ raise InvalidSocketError unless VALID_SOCKET_CLASSES.any? { |klass| socket.is_a?(klass) }
36
+ super(nil)
37
+ @logdev = LogDevice.new(socket)
38
+ end
39
+
40
+ # implements an interface similar to ::Logger::LogDevice. This is the recipient of the
41
+ # formatted log messages
42
+ class LogDevice
43
+ # :nodoc:
44
+ def initialize(socket)
45
+ @socket = socket
46
+ end
47
+
48
+ # write the formatted log message to the socket.
49
+ def write(message)
50
+ @socket.send(message)
51
+ end
52
+
53
+ def close
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module ZMQ
4
- VERSION = "1.6.4"
5
- end
4
+ VERSION = "1.7.0"
5
+ end
@@ -11,10 +11,9 @@ class TestZmqFrame < ZmqTestCase
11
11
 
12
12
  def test_destroyed_frame
13
13
  frame = ZMQ::Frame("message")
14
+ assert !frame.gone?
14
15
  frame.destroy
15
- assert_raises ZMQ::Error do
16
- frame.data
17
- end
16
+ assert frame.gone?
18
17
  end
19
18
 
20
19
  def test_alloc_empty
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), 'helper')
4
+
5
+ class TestZmqLogger < ZmqTestCase
6
+
7
+ def setup
8
+ @ctx = ZMQ::Context.new
9
+ end
10
+
11
+ def teardown
12
+ @ctx.destroy
13
+ end
14
+
15
+ def test_invalid_socket
16
+ socket = @ctx.socket(ZMQ::REQ)
17
+ assert_raises ZMQ::Logger::InvalidSocketError do
18
+ ZMQ::Logger.new(socket)
19
+ end
20
+ end
21
+
22
+ def test_valid_socket
23
+ socket = @ctx.socket(ZMQ::PUSH)
24
+ assert_nothing_raised do
25
+ ZMQ::Logger.new(socket)
26
+ end
27
+ end
28
+
29
+ def test_send_message
30
+ reader = @ctx.socket(ZMQ::PULL)
31
+ port = reader.bind("tcp://*:*")
32
+ assert port > 0
33
+ writer = @ctx.socket(ZMQ::PUSH)
34
+ writer.connect("tcp://localhost:#{port}")
35
+ logger = ZMQ::Logger.new(writer)
36
+ assert logger.debug("hellö world")
37
+ message = reader.recv.force_encoding Encoding::UTF_8
38
+ assert_not_nil message =~ /hellö world/
39
+ end
40
+
41
+ end
@@ -11,10 +11,11 @@ class TestZmqMessage < ZmqTestCase
11
11
 
12
12
  def test_destroyed
13
13
  msg = ZMQ::Message("one", "two")
14
+ assert_not_nil msg.encode
15
+ assert !msg.gone?
14
16
  msg.destroy
15
- assert_raises ZMQ::Error do
16
- msg.encode
17
- end
17
+ assert msg.gone?
18
+ assert_nil msg.encode
18
19
  end
19
20
 
20
21
  def test_message_sugar
@@ -182,7 +183,7 @@ class TestZmqMessage < ZmqTestCase
182
183
  end
183
184
 
184
185
  def test_equals
185
- msg = ZMQ::Message.new
186
+ msg = ZMQ::Message.new
186
187
  msg.pushstr "body"
187
188
  msg.pushstr "header"
188
189
 
@@ -198,4 +199,96 @@ class TestZmqMessage < ZmqTestCase
198
199
  assert !msg.eql?(other)
199
200
  assert other.eql?(other)
200
201
  end
201
- end
202
+
203
+ def test_message_with_frames
204
+ msg = ZMQ::Message.new
205
+ frame = ZMQ::Frame.new("hello")
206
+ assert_equal "hello", frame.data
207
+ msg.add frame
208
+ # frame is owned by message, but message is still owned by ruby,
209
+ # so the frame data should still be accessible:
210
+ assert_equal "hello", frame.data
211
+ end
212
+
213
+ def test_messge_add_frame_twice
214
+ msg = ZMQ::Message.new
215
+ frame = ZMQ::Frame.new("hello")
216
+ msg.add frame
217
+ assert_raises ZMQ::Error do
218
+ msg.add frame
219
+ end
220
+ end
221
+
222
+ def test_message_is_gone_after_send
223
+ ctx = ZMQ::Context.new
224
+ endpoint = "inproc://test.test_message_is_gone_after_send"
225
+ push = ctx.bind(:PUSH, endpoint)
226
+ pull = ctx.connect(:PULL, endpoint)
227
+ msg = ZMQ::Message.new
228
+ frame = ZMQ::Frame.new("hello")
229
+ msg.add frame
230
+ push.send_message(msg)
231
+ assert msg.gone?
232
+ assert frame.gone?
233
+ ensure
234
+ ctx.destroy
235
+ end
236
+
237
+ def test_message_has_frames_on_receive
238
+ ctx = ZMQ::Context.new
239
+ endpoint = "inproc://test.test_message_is_gone_after_send"
240
+ push = ctx.bind(:PUSH, endpoint)
241
+ pull = ctx.connect(:PULL, endpoint)
242
+ msg = ZMQ::Message.new
243
+ msg.add ZMQ::Frame.new("hello")
244
+ msg.add ZMQ::Frame.new("world")
245
+ push.send_message(msg)
246
+ received = pull.recv_message
247
+ assert_not_nil received
248
+ assert_equal 2, received.size
249
+ assert_equal "hello", received.first.data
250
+ assert_equal "world", received.next.data
251
+ ensure
252
+ ctx.destroy
253
+ end
254
+
255
+ def test_message_remove_frame
256
+ msg = ZMQ::Message.new
257
+ frame = ZMQ::Frame.new("hello")
258
+ msg.add frame
259
+ assert_equal 1, msg.size
260
+ # same object is returned
261
+ assert_equal frame.object_id, msg.first.object_id
262
+ msg.remove frame
263
+ assert_equal 0, msg.size
264
+ end
265
+
266
+ def test_message_array_same_frame_objects
267
+ msg = ZMQ::Message.new
268
+ frame = ZMQ::Frame.new("hello")
269
+ msg.add frame
270
+ ary = msg.to_a
271
+ assert_equal 1, ary.count
272
+ assert_equal frame, ary.first
273
+ assert_equal frame.object_id, ary.first.object_id
274
+ end
275
+
276
+ def test_message_unwrap_dup
277
+ ctx = ZMQ::Context.new
278
+ router = ctx.socket(ZMQ::ROUTER)
279
+ port = router.bind("tcp://127.0.0.1:*")
280
+ req = ctx.connect(ZMQ::REQ, "tcp://127.0.0.1:#{port}")
281
+ msg = ZMQ::Message.new
282
+ msg.addstr "hello world"
283
+ req.send_message msg
284
+ msg2 = router.recv_message
285
+ identity = msg2.unwrap
286
+ assert_equal "hello world", msg2.first.data
287
+ msg3 = ZMQ::Message.new
288
+ msg3.addstr "reply"
289
+ msg3.wrap identity.dup
290
+ assert_equal 3, msg3.size
291
+ ensure
292
+ ctx.destroy
293
+ end
294
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbczmq
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.4
5
- prerelease:
4
+ version: 1.7.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Lourens Naudé
@@ -11,12 +10,11 @@ authors:
11
10
  autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2013-07-09 00:00:00.000000000 Z
13
+ date: 2013-07-21 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: rake-compiler
18
17
  requirement: !ruby/object:Gem::Requirement
19
- none: false
20
18
  requirements:
21
19
  - - ~>
22
20
  - !ruby/object:Gem::Version
@@ -24,7 +22,6 @@ dependencies:
24
22
  type: :development
25
23
  prerelease: false
26
24
  version_requirements: !ruby/object:Gem::Requirement
27
- none: false
28
25
  requirements:
29
26
  - - ~>
30
27
  - !ruby/object:Gem::Version
@@ -86,6 +83,7 @@ files:
86
83
  - lib/zmq/default_handler.rb
87
84
  - lib/zmq/frame.rb
88
85
  - lib/zmq/handler.rb
86
+ - lib/zmq/logger.rb
89
87
  - lib/zmq/loop.rb
90
88
  - lib/zmq/message.rb
91
89
  - lib/zmq/monitor.rb
@@ -136,6 +134,7 @@ files:
136
134
  - test/test_context.rb
137
135
  - test/test_frame.rb
138
136
  - test/test_handler.rb
137
+ - test/test_logger.rb
139
138
  - test/test_loop.rb
140
139
  - test/test_message.rb
141
140
  - test/test_monitoring.rb
@@ -529,28 +528,27 @@ files:
529
528
  - ext/zeromq/version.sh
530
529
  homepage: http://github.com/methodmissing/rbczmq
531
530
  licenses: []
531
+ metadata: {}
532
532
  post_install_message:
533
533
  rdoc_options:
534
534
  - --charset=UTF-8
535
535
  require_paths:
536
536
  - lib
537
537
  required_ruby_version: !ruby/object:Gem::Requirement
538
- none: false
539
538
  requirements:
540
539
  - - ! '>='
541
540
  - !ruby/object:Gem::Version
542
541
  version: '0'
543
542
  required_rubygems_version: !ruby/object:Gem::Requirement
544
- none: false
545
543
  requirements:
546
544
  - - ! '>='
547
545
  - !ruby/object:Gem::Version
548
546
  version: '0'
549
547
  requirements: []
550
548
  rubyforge_project:
551
- rubygems_version: 1.8.24
549
+ rubygems_version: 2.0.3
552
550
  signing_key:
553
- specification_version: 3
551
+ specification_version: 4
554
552
  summary: Ruby extension for ZeroMQ (ZMQ) using CZMQ - High-level C Binding for ØMQ
555
553
  (http://czmq.zeromq.org)
556
554
  test_files:
@@ -573,6 +571,7 @@ test_files:
573
571
  - test/test_context.rb
574
572
  - test/test_frame.rb
575
573
  - test/test_handler.rb
574
+ - test/test_logger.rb
576
575
  - test/test_loop.rb
577
576
  - test/test_message.rb
578
577
  - test/test_monitoring.rb
@@ -582,4 +581,3 @@ test_files:
582
581
  - test/test_threading.rb
583
582
  - test/test_timer.rb
584
583
  - test/test_zmq.rb
585
- has_rdoc: true