rbczmq 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/.gitignore +23 -0
  2. data/.travis.yml +19 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +19 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +247 -0
  7. data/Rakefile +67 -0
  8. data/examples/loop.rb +109 -0
  9. data/examples/poller.rb +37 -0
  10. data/examples/pub_sub.rb +101 -0
  11. data/examples/push_pull.rb +104 -0
  12. data/examples/req_rep.rb +100 -0
  13. data/ext/czmq.tar.gz +0 -0
  14. data/ext/rbczmq/context.c +280 -0
  15. data/ext/rbczmq/context.h +26 -0
  16. data/ext/rbczmq/extconf.rb +138 -0
  17. data/ext/rbczmq/frame.c +401 -0
  18. data/ext/rbczmq/frame.h +24 -0
  19. data/ext/rbczmq/jruby.h +22 -0
  20. data/ext/rbczmq/loop.c +413 -0
  21. data/ext/rbczmq/loop.h +24 -0
  22. data/ext/rbczmq/message.c +620 -0
  23. data/ext/rbczmq/message.h +24 -0
  24. data/ext/rbczmq/poller.c +308 -0
  25. data/ext/rbczmq/poller.h +29 -0
  26. data/ext/rbczmq/pollitem.c +251 -0
  27. data/ext/rbczmq/pollitem.h +25 -0
  28. data/ext/rbczmq/rbczmq_ext.c +198 -0
  29. data/ext/rbczmq/rbczmq_ext.h +94 -0
  30. data/ext/rbczmq/rbczmq_prelude.h +22 -0
  31. data/ext/rbczmq/rubinius.h +24 -0
  32. data/ext/rbczmq/ruby18.h +43 -0
  33. data/ext/rbczmq/ruby19.h +15 -0
  34. data/ext/rbczmq/socket.c +1570 -0
  35. data/ext/rbczmq/socket.h +136 -0
  36. data/ext/rbczmq/timer.c +110 -0
  37. data/ext/rbczmq/timer.h +23 -0
  38. data/ext/zeromq.tar.gz +0 -0
  39. data/lib/rbczmq.rb +3 -0
  40. data/lib/zmq.rb +77 -0
  41. data/lib/zmq/context.rb +50 -0
  42. data/lib/zmq/default_handler.rb +16 -0
  43. data/lib/zmq/frame.rb +11 -0
  44. data/lib/zmq/handler.rb +76 -0
  45. data/lib/zmq/loop.rb +131 -0
  46. data/lib/zmq/message.rb +9 -0
  47. data/lib/zmq/poller.rb +22 -0
  48. data/lib/zmq/pollitem.rb +31 -0
  49. data/lib/zmq/socket.rb +125 -0
  50. data/lib/zmq/socket/dealer.rb +33 -0
  51. data/lib/zmq/socket/pair.rb +39 -0
  52. data/lib/zmq/socket/pub.rb +30 -0
  53. data/lib/zmq/socket/pull.rb +29 -0
  54. data/lib/zmq/socket/push.rb +32 -0
  55. data/lib/zmq/socket/rep.rb +37 -0
  56. data/lib/zmq/socket/req.rb +37 -0
  57. data/lib/zmq/socket/router.rb +38 -0
  58. data/lib/zmq/socket/sub.rb +27 -0
  59. data/lib/zmq/timer.rb +12 -0
  60. data/lib/zmq/version.rb +5 -0
  61. data/perf/pair.rb +7 -0
  62. data/perf/pair/local.rb +22 -0
  63. data/perf/pair/remote.rb +25 -0
  64. data/perf/pub_sub.rb +7 -0
  65. data/perf/pub_sub/local.rb +22 -0
  66. data/perf/pub_sub/remote.rb +25 -0
  67. data/perf/push_pull.rb +7 -0
  68. data/perf/push_pull/local.rb +21 -0
  69. data/perf/push_pull/remote.rb +25 -0
  70. data/perf/req_rep.rb +7 -0
  71. data/perf/req_rep/local.rb +35 -0
  72. data/perf/req_rep/remote.rb +28 -0
  73. data/perf/runner.rb +142 -0
  74. data/rbczmq.gemspec +22 -0
  75. data/test/helper.rb +21 -0
  76. data/test/socket/test_dealer_socket.rb +14 -0
  77. data/test/socket/test_pair_socket.rb +24 -0
  78. data/test/socket/test_pair_sockets.rb +74 -0
  79. data/test/socket/test_pub_socket.rb +17 -0
  80. data/test/socket/test_pub_sub_sockets.rb +87 -0
  81. data/test/socket/test_pull_socket.rb +17 -0
  82. data/test/socket/test_push_pull_sockets.rb +81 -0
  83. data/test/socket/test_push_socket.rb +17 -0
  84. data/test/socket/test_rep_socket.rb +25 -0
  85. data/test/socket/test_req_rep_sockets.rb +42 -0
  86. data/test/socket/test_req_socket.rb +27 -0
  87. data/test/socket/test_router_socket.rb +14 -0
  88. data/test/socket/test_routing.rb +66 -0
  89. data/test/socket/test_sub_socket.rb +17 -0
  90. data/test/test_context.rb +86 -0
  91. data/test/test_frame.rb +78 -0
  92. data/test/test_handler.rb +28 -0
  93. data/test/test_loop.rb +252 -0
  94. data/test/test_message.rb +201 -0
  95. data/test/test_poller.rb +154 -0
  96. data/test/test_pollitem.rb +78 -0
  97. data/test/test_socket.rb +403 -0
  98. data/test/test_threading.rb +34 -0
  99. data/test/test_timer.rb +37 -0
  100. data/test/test_zmq.rb +62 -0
  101. metadata +208 -0
@@ -0,0 +1,24 @@
1
+ #ifndef RBCZMQ_FRAME_H
2
+ #define RBCZMQ_FRAME_H
3
+
4
+ #define ZmqAssertFrame(obj) ZmqAssertType(obj, rb_cZmqFrame, "ZMQ::Frame")
5
+ #define ZmqGetFrame(obj) \
6
+ zframe_t *frame = NULL; \
7
+ 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);
11
+
12
+ #define ZmqRegisterFrame(fr) \
13
+ zframe_freefn((fr), rb_czmq_frame_freed); \
14
+ st_insert(frames_map, (st_data_t)(fr), (st_data_t)0);
15
+
16
+ void rb_czmq_free_frame(zframe_t *frame);
17
+ void rb_czmq_free_frame_gc(void *ptr);
18
+ void rb_czmq_frame_freed(zframe_t *frame);
19
+
20
+ VALUE rb_czmq_alloc_frame(zframe_t *frame);
21
+
22
+ void _init_rb_czmq_frame();
23
+
24
+ #endif
@@ -0,0 +1,22 @@
1
+ #ifndef RBCZMQ_JRUBY_H
2
+ #define RBCZMQ_JRUBY_H
3
+
4
+ #include "st.h"
5
+
6
+ /* XXX */
7
+ #define ZmqEncode(str) str
8
+ #ifndef THREAD_PASS
9
+ #define THREAD_PASS rb_thread_schedule();
10
+ #endif
11
+
12
+ #define TRAP_BEG
13
+ #define TRAP_END
14
+
15
+ #undef rb_errinfo
16
+ #define rb_errinfo() (rb_gv_get("$!"))
17
+
18
+ #define ZMQ_DEFAULT_SOCKET_TIMEOUT Qnil
19
+
20
+ #define HAVE_RB_THREAD_BLOCKING_REGION 1
21
+
22
+ #endif
data/ext/rbczmq/loop.c ADDED
@@ -0,0 +1,413 @@
1
+ #include <rbczmq_ext.h>
2
+
3
+ static VALUE intern_call;
4
+ static VALUE intern_readable;
5
+ static VALUE intern_writable;
6
+ static VALUE intern_error;
7
+
8
+ /*
9
+ * :nodoc:
10
+ * Wraps rb_funcall to support callbacks with or without callbacks.
11
+ *
12
+ */
13
+ static VALUE rb_czmq_callback0(VALUE *args)
14
+ {
15
+ return (NIL_P(args[2])) ? rb_funcall(args[0], args[1], 0) : rb_funcall(args[0], args[1], 1, args[2]);
16
+ }
17
+
18
+ /*
19
+ * :nodoc:
20
+ * Callback for when the reactor started. Invoked from a a oneshot timer that fires after 1ms and updates the running
21
+ * member on the loop struct.
22
+ *
23
+ */
24
+ ZMQ_NOINLINE static int rb_czmq_loop_started_callback(ZMQ_UNUSED zloop_t *loop, ZMQ_UNUSED zmq_pollitem_t *item, void *arg)
25
+ {
26
+ zmq_loop_wrapper *loop_wrapper = arg;
27
+ loop_wrapper->running = TRUE;
28
+ return 0;
29
+ }
30
+
31
+ /*
32
+ * :nodoc:
33
+ * Callback to signal / break the reactor loop. Triggered when we invoke ZMQ::Loop#stop. The -1 return will trickle down
34
+ * to libczmq and stop the event loop.
35
+ *
36
+ */
37
+ ZMQ_NOINLINE static int rb_czmq_loop_breaker_callback(ZMQ_UNUSED zloop_t *loop, ZMQ_UNUSED zmq_pollitem_t *item, void *arg)
38
+ {
39
+ zmq_loop_wrapper *loop_wrapper = arg;
40
+ loop_wrapper->running = FALSE;
41
+ return -1;
42
+ }
43
+
44
+ /*
45
+ * :nodoc:
46
+ * Wraps calls back into the Ruby VM with rb_protect and properly bubbles up an errors to the user.
47
+ *
48
+ */
49
+ ZMQ_NOINLINE static int rb_czmq_callback(zloop_t *loop, VALUE *args)
50
+ {
51
+ int status;
52
+ volatile VALUE ret;
53
+ status = 0;
54
+ ret = rb_protect((VALUE(*)(VALUE))rb_czmq_callback0, (VALUE)args, &status);
55
+ if (status) {
56
+ zloop_timer(loop, 1, 1, rb_czmq_loop_breaker_callback, (void *)args[0]);
57
+ if (NIL_P(rb_errinfo())) {
58
+ rb_jump_tag(status);
59
+ } else {
60
+ rb_funcall(args[0], intern_error, 1, rb_errinfo());
61
+ return 0;
62
+ }
63
+ } else if (ret == Qfalse) {
64
+ zloop_timer(loop, 1, 1, rb_czmq_loop_breaker_callback, (void *)args[0]);
65
+ return -1;
66
+ }
67
+ return 0;
68
+ }
69
+
70
+ /*
71
+ * :nodoc:
72
+ * Low level callback for when timers registered with the reactor fires. This calls back into the Ruby VM.
73
+ *
74
+ */
75
+ ZMQ_NOINLINE static int rb_czmq_loop_timer_callback(zloop_t *loop, ZMQ_UNUSED zmq_pollitem_t *item, void *cb)
76
+ {
77
+ int rc;
78
+ VALUE args[3];
79
+ ZmqGetTimer((VALUE)cb);
80
+ if (timer->cancelled == TRUE) {
81
+ zloop_timer_end(loop, (void *)cb);
82
+ return 0;
83
+ }
84
+ args[0] = (VALUE)cb;
85
+ args[1] = intern_call;
86
+ args[2] = Qnil;
87
+ rc = rb_czmq_callback(loop, args);
88
+ return rc;
89
+ }
90
+
91
+ /*
92
+ * :nodoc:
93
+ * Low level callback for handling socket activity. This calls back into the Ruby VM. We special case ZMQ_POLLERR
94
+ * by invoking the error callback on the handler registered for this socket.
95
+ *
96
+ */
97
+ ZMQ_NOINLINE static int rb_czmq_loop_pollitem_callback(zloop_t *loop, zmq_pollitem_t *item, void *arg)
98
+ {
99
+ int ret_r = 0;
100
+ int ret_w = 0;
101
+ int ret_e = 0;
102
+ VALUE args[3];
103
+ zmq_pollitem_wrapper *pollitem = (zmq_pollitem_wrapper *)arg;
104
+ args[0] = (VALUE)pollitem->handler;
105
+ args[2] = Qnil;
106
+ if (item->revents & ZMQ_POLLIN) {
107
+ args[1] = intern_readable;
108
+ ret_r = rb_czmq_callback(loop, args);
109
+ }
110
+ if (item->revents & ZMQ_POLLOUT) {
111
+ args[1] = intern_writable;
112
+ ret_w = rb_czmq_callback(loop, args);
113
+ }
114
+ if (item->revents & ZMQ_POLLERR) {
115
+ args[1] = intern_error;
116
+ args[2] = rb_exc_new2(rb_eZmqError, zmq_strerror(zmq_errno()));
117
+ ret_e = rb_czmq_callback(loop, args);
118
+ }
119
+ if (ret_r == -1 || ret_w == -1 || ret_e == -1) return -1;
120
+ return 0;
121
+ }
122
+
123
+ static void rb_czmq_loop_stop0(zmq_loop_wrapper *loop);
124
+
125
+ /*
126
+ * :nodoc:
127
+ * Free all resources for a reactor loop - invoked by the lower level ZMQ::Loop#destroy as well as the GC callback
128
+ *
129
+ */
130
+ static void rb_czmq_free_loop(zmq_loop_wrapper *loop)
131
+ {
132
+ rb_czmq_loop_stop0(loop);
133
+ zloop_destroy(&(loop->loop));
134
+ loop->loop = NULL;
135
+ loop->flags |= ZMQ_LOOP_DESTROYED;
136
+ }
137
+
138
+ /*
139
+ * :nodoc:
140
+ * GC free callback
141
+ *
142
+ */
143
+ static void rb_czmq_free_loop_gc(void *ptr)
144
+ {
145
+ zmq_loop_wrapper *loop = (zmq_loop_wrapper *)ptr;
146
+ if (loop) {
147
+ if (loop->loop != NULL && !(loop->flags & ZMQ_LOOP_DESTROYED)) rb_czmq_free_loop(loop);
148
+ xfree(loop);
149
+ }
150
+ }
151
+
152
+ /*
153
+ * call-seq:
154
+ * ZMQ::Loop.new => ZMQ::Loop
155
+ *
156
+ * Creates a new reactor instance. Several loops per process is supported for the lower level API.
157
+ *
158
+ * === Examples
159
+ * ZMQ::Loop.new => ZMQ::Loop
160
+ *
161
+ */
162
+
163
+ static VALUE rb_czmq_loop_new(VALUE loop)
164
+ {
165
+ zmq_loop_wrapper *lp = NULL;
166
+ errno = 0;
167
+ loop = Data_Make_Struct(rb_cZmqLoop, zmq_loop_wrapper, 0, rb_czmq_free_loop_gc, lp);
168
+ lp->loop = zloop_new();
169
+ ZmqAssertObjOnAlloc(lp->loop, lp);
170
+ lp->flags = 0;
171
+ lp->running = FALSE;
172
+ lp->verbose = FALSE;
173
+ rb_obj_call_init(loop, 0, NULL);
174
+ return loop;
175
+ }
176
+
177
+ /*
178
+ * call-seq:
179
+ * loop.start => Fixnum
180
+ *
181
+ * Creates a new reactor instance and blocks the caller until the process is interrupted, the context terminates or the
182
+ * loop's explicitly stopped via callback. Returns 0 if interrupted and -1 when stopped via a handler.
183
+ *
184
+ * === Examples
185
+ * loop = ZMQ::Loop.new => ZMQ::Loop
186
+ * loop.start => Fixnum
187
+ *
188
+ */
189
+
190
+ static VALUE rb_czmq_loop_start(VALUE obj)
191
+ {
192
+ int rc;
193
+ errno = 0;
194
+ ZmqGetLoop(obj);
195
+ THREAD_PASS;
196
+ zloop_timer(loop->loop, 1, 1, rb_czmq_loop_started_callback, loop);
197
+ rc = zloop_start(loop->loop);
198
+ if (rc > 0) rb_raise(rb_eZmqError, "internal event loop error!");
199
+ return INT2NUM(rc);
200
+ }
201
+
202
+ /*
203
+ * call-seq:
204
+ * loop.running? => boolean
205
+ *
206
+ * Predicate that returns true if the reactor is currently running.
207
+ *
208
+ * === Examples
209
+ * loop = ZMQ::Loop.new => ZMQ::Loop
210
+ * loop.running? => false
211
+ *
212
+ */
213
+
214
+ static VALUE rb_czmq_loop_running_p(VALUE obj)
215
+ {
216
+ ZmqGetLoop(obj);
217
+ return (loop->running == TRUE) ? Qtrue : Qfalse;
218
+ }
219
+
220
+ /*
221
+ * :nodoc:
222
+ * Registers a oneshot timer that'll fire after 1msec to signal a loop break.
223
+ *
224
+ */
225
+ static void rb_czmq_loop_stop0(zmq_loop_wrapper *loop)
226
+ {
227
+ zloop_timer(loop->loop, 1, 1, rb_czmq_loop_breaker_callback, loop);
228
+ }
229
+
230
+ /*
231
+ * call-seq:
232
+ * loop.stop => nil
233
+ *
234
+ * Stops the reactor loop. ZMQ::Loop#start will return a -1 status code as this can only be called via a handler.
235
+ *
236
+ * === Examples
237
+ * loop = ZMQ::Loop.new => ZMQ::Loop
238
+ * loop.add_timer(1){ loop.stop }
239
+ * loop.start => -1
240
+ *
241
+ */
242
+
243
+ static VALUE rb_czmq_loop_stop(VALUE obj)
244
+ {
245
+ ZmqGetLoop(obj);
246
+ if (loop->running == FALSE) rb_raise(rb_eZmqError, "event loop not running!");
247
+ rb_czmq_loop_stop0(loop);
248
+ return Qnil;
249
+ }
250
+
251
+ /*
252
+ * call-seq:
253
+ * loop.destroy => nil
254
+ *
255
+ * Explicitly destroys a reactor instance. Useful for manual memory management, otherwise the GC
256
+ * will take the same action if a message object is not reachable anymore on the next GC cycle. This is
257
+ * a lower level API.
258
+ *
259
+ * === Examples
260
+ * loop = ZMQ::Loop.new => ZMQ::Loop
261
+ * loop.destroy => nil
262
+ *
263
+ */
264
+
265
+ static VALUE rb_czmq_loop_destroy(VALUE obj)
266
+ {
267
+ ZmqGetLoop(obj);
268
+ rb_czmq_free_loop(loop);
269
+ return Qnil;
270
+ }
271
+
272
+ /*
273
+ * call-seq:
274
+ * loop.verbose = true => nil
275
+ *
276
+ * Logs reactor activity to stdout - useful for debugging, but can be quite noisy with lots of activity.
277
+ *
278
+ * === Examples
279
+ * loop = ZMQ::Loop.new => ZMQ::Loop
280
+ * loop.verbose = true => nil
281
+ *
282
+ */
283
+
284
+ static VALUE rb_czmq_loop_set_verbose(VALUE obj, VALUE level)
285
+ {
286
+ Bool vlevel;
287
+ ZmqGetLoop(obj);
288
+ vlevel = (level == Qtrue) ? TRUE : FALSE;
289
+ zloop_set_verbose(loop->loop, vlevel);
290
+ loop->verbose = vlevel;
291
+ return Qnil;
292
+ }
293
+
294
+ /*
295
+ * call-seq:
296
+ * loop.register(item) => true
297
+ *
298
+ * Registers a poll item with the reactor. Only ZMQ::POLLIN and ZMQ::POLLOUT events are supported.
299
+ *
300
+ * === Examples
301
+ * loop = ZMQ::Loop.new => ZMQ::Loop
302
+ * item = ZMQ::Pollitem.new(sock, ZMQ::POLLIN) => ZMQ::Pollitem
303
+ * loop.register(item) => true
304
+ *
305
+ */
306
+
307
+ static VALUE rb_czmq_loop_register(VALUE obj, VALUE pollable)
308
+ {
309
+ int rc;
310
+ errno = 0;
311
+ ZmqGetLoop(obj);
312
+ pollable = rb_czmq_pollitem_coerce(pollable);
313
+ ZmqGetPollitem(pollable);
314
+ rc = zloop_poller(loop->loop, pollitem->item, rb_czmq_loop_pollitem_callback, (void *)pollitem);
315
+ ZmqAssert(rc);
316
+ /* Let pollable be verbose if loop is verbose */
317
+ if (loop->verbose == TRUE) rb_czmq_pollitem_set_verbose(pollable, Qtrue);
318
+ return Qtrue;
319
+ }
320
+
321
+ /*
322
+ * call-seq:
323
+ * loop.remove(item) => nil
324
+ *
325
+ * Removes a previously registered poll item from the reactor loop.
326
+ *
327
+ * === Examples
328
+ * loop = ZMQ::Loop.new => ZMQ::Loop
329
+ * item = ZMQ::Pollitem.new(sock, ZMQ::POLLIN) => ZMQ::Pollitem
330
+ * loop.register(item) => true
331
+ * loop.remove(item) => nil
332
+ *
333
+ */
334
+
335
+ static VALUE rb_czmq_loop_remove(VALUE obj, VALUE pollable)
336
+ {
337
+ errno = 0;
338
+ ZmqGetLoop(obj);
339
+ pollable = rb_czmq_pollitem_coerce(pollable);
340
+ ZmqGetPollitem(pollable);
341
+ zloop_poller_end(loop->loop, pollitem->item);
342
+ return Qnil;
343
+ }
344
+
345
+ /*
346
+ * call-seq:
347
+ * loop.register_timer(timer) => true
348
+ *
349
+ * Registers a ZMQ::Timer instance with the reactor.
350
+ *
351
+ * === Examples
352
+ * loop = ZMQ::Loop.new => ZMQ::Loop
353
+ * timer = ZMQ::Timer.new(1, 2){ :fired }
354
+ * loop.register_timer(timer) => true
355
+ *
356
+ */
357
+
358
+ static VALUE rb_czmq_loop_register_timer(VALUE obj, VALUE tm)
359
+ {
360
+ int rc;
361
+ errno = 0;
362
+ ZmqGetLoop(obj);
363
+ ZmqGetTimer(tm);
364
+ rc = zloop_timer(loop->loop, timer->delay, timer->times, rb_czmq_loop_timer_callback, (void *)tm);
365
+ ZmqAssert(rc);
366
+ return Qtrue;
367
+ }
368
+
369
+ /*
370
+ * call-seq:
371
+ * loop.cancel_timer(timer) => nil
372
+ *
373
+ * Cancels a ZMQ::Timer instance previously registered with the reactor.
374
+ *
375
+ * === Examples
376
+ * loop = ZMQ::Loop.new => ZMQ::Loop
377
+ * timer = ZMQ::Timer.new(1, 2){ :fired }
378
+ * loop.register_timer(timer) => true
379
+ * loop.cancel_timer(timer) => nil
380
+ *
381
+ */
382
+
383
+ static VALUE rb_czmq_loop_cancel_timer(VALUE obj, VALUE tm)
384
+ {
385
+ int rc;
386
+ errno = 0;
387
+ ZmqGetLoop(obj);
388
+ ZmqGetTimer(tm);
389
+ rc = zloop_timer_end(loop->loop, (void *)tm);
390
+ ZmqAssert(rc);
391
+ return Qtrue;
392
+ }
393
+
394
+ void _init_rb_czmq_loop()
395
+ {
396
+ intern_call = rb_intern("call");
397
+ intern_readable = rb_intern("on_readable");
398
+ intern_writable = rb_intern("on_writable");
399
+ intern_error = rb_intern("on_error");
400
+
401
+ rb_cZmqLoop = rb_define_class_under(rb_mZmq, "Loop", rb_cObject);
402
+
403
+ rb_define_alloc_func(rb_cZmqLoop, rb_czmq_loop_new);
404
+ rb_define_method(rb_cZmqLoop, "start", rb_czmq_loop_start, 0);
405
+ rb_define_method(rb_cZmqLoop, "stop", rb_czmq_loop_stop, 0);
406
+ rb_define_method(rb_cZmqLoop, "running?", rb_czmq_loop_running_p, 0);
407
+ rb_define_method(rb_cZmqLoop, "destroy", rb_czmq_loop_destroy, 0);
408
+ rb_define_method(rb_cZmqLoop, "verbose=", rb_czmq_loop_set_verbose, 1);
409
+ rb_define_method(rb_cZmqLoop, "register", rb_czmq_loop_register, 1);
410
+ rb_define_method(rb_cZmqLoop, "remove", rb_czmq_loop_remove, 1);
411
+ rb_define_method(rb_cZmqLoop, "register_timer", rb_czmq_loop_register_timer, 1);
412
+ rb_define_method(rb_cZmqLoop, "cancel_timer", rb_czmq_loop_cancel_timer, 1);
413
+ }