slyphon-zookeeper 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/zookeeper_c.c ADDED
@@ -0,0 +1,571 @@
1
+ /* Ruby wrapper for the Zookeeper C API
2
+ * Phillip Pearson <pp@myelin.co.nz>
3
+ * Eric Maland <eric@twitter.com>
4
+ * Brian Wickman <wickman@twitter.com>
5
+ *
6
+ * This fork is a 90% rewrite of the original. It takes a more evented
7
+ * approach to isolate the ZK state machine from the ruby interpreter via an
8
+ * event queue. It's similar to the ZookeeperFFI version except that it
9
+ * actually works on MRI 1.8.
10
+ */
11
+
12
+ #define THREADED
13
+
14
+ #include "ruby.h"
15
+ #include "c-client-src/zookeeper.h"
16
+ #include <errno.h>
17
+ #include <stdio.h>
18
+ #include <stdlib.h>
19
+ #include <unistd.h>
20
+
21
+ #include "zookeeper_lib.h"
22
+
23
+ static VALUE Zookeeper = Qnil;
24
+
25
+ struct zkrb_instance_data {
26
+ zhandle_t *zh;
27
+ clientid_t myid;
28
+ zkrb_queue_t *queue;
29
+ };
30
+
31
+ typedef enum {
32
+ SYNC = 0,
33
+ ASYNC = 1,
34
+ SYNC_WATCH = 2,
35
+ ASYNC_WATCH = 3
36
+ } zkrb_call_type;
37
+
38
+ #define IS_SYNC(zkrbcall) ((zkrbcall)==SYNC || (zkrbcall)==SYNC_WATCH)
39
+ #define IS_ASYNC(zkrbcall) ((zkrbcall)==ASYNC || (zkrbcall)==ASYNC_WATCH)
40
+
41
+ static int destroy_zkrb_instance(struct zkrb_instance_data* ptr) {
42
+ int rv = ZOK;
43
+
44
+ if (ptr->zh) {
45
+ const void *ctx = zoo_get_context(ptr->zh);
46
+ /* Note that after zookeeper_close() returns, ZK handle is invalid */
47
+ rv = zookeeper_close(ptr->zh);
48
+ free((void *) ctx);
49
+ }
50
+
51
+ #warning [wickman] TODO: fire off warning if queue is not empty
52
+ if (ptr->queue) zkrb_queue_free(ptr->queue);
53
+
54
+ ptr->zh = NULL;
55
+ ptr->queue = NULL;
56
+ return rv;
57
+ }
58
+
59
+ static void free_zkrb_instance_data(struct zkrb_instance_data* ptr) {
60
+ destroy_zkrb_instance(ptr);
61
+ }
62
+
63
+ static void print_zkrb_instance_data(struct zkrb_instance_data* ptr) {
64
+ fprintf(stderr, "zkrb_instance_data (%p) {\n", ptr);
65
+ fprintf(stderr, " zh = %p\n", ptr->zh);
66
+ fprintf(stderr, " { state = %d }\n", zoo_state(ptr->zh));
67
+ fprintf(stderr, " id = %llx\n", ptr->myid.client_id);
68
+ fprintf(stderr, " q = %p\n", ptr->queue);
69
+ fprintf(stderr, "}\n");
70
+ }
71
+
72
+ static VALUE method_init(int argc, VALUE* argv, VALUE self) {
73
+ VALUE hostPort;
74
+ VALUE options;
75
+ rb_scan_args(argc, argv, "11", &hostPort, &options);
76
+
77
+ if (NIL_P(options)) {
78
+ options = rb_hash_new();
79
+ } else {
80
+ Check_Type(options, T_HASH);
81
+ }
82
+
83
+ Check_Type(hostPort, T_STRING);
84
+
85
+ // Look up :zkc_log_level
86
+ VALUE log_level = rb_hash_aref(options, ID2SYM(rb_intern("zkc_log_level")));
87
+ if (NIL_P(log_level)) {
88
+ zoo_set_debug_level(0); // no log messages
89
+ } else {
90
+ Check_Type(log_level, T_FIXNUM);
91
+ zoo_set_debug_level(log_level);
92
+ }
93
+
94
+
95
+ VALUE data;
96
+ struct zkrb_instance_data *zk_local_ctx;
97
+ data = Data_Make_Struct(Zookeeper,
98
+ struct zkrb_instance_data,
99
+ 0,
100
+ free_zkrb_instance_data,
101
+ zk_local_ctx);
102
+ zk_local_ctx->queue = zkrb_queue_alloc();
103
+
104
+ zoo_deterministic_conn_order(0);
105
+
106
+ zkrb_calling_context *ctx =
107
+ zkrb_calling_context_alloc(ZKRB_GLOBAL_REQ, zk_local_ctx->queue);
108
+
109
+ zk_local_ctx->zh =
110
+ zookeeper_init(
111
+ RSTRING_PTR(hostPort),
112
+ zkrb_state_callback,
113
+ 10000,
114
+ &zk_local_ctx->myid,
115
+ ctx,
116
+ 0);
117
+
118
+ #warning [wickman] TODO handle this properly on the Ruby side rather than C side
119
+ if (!zk_local_ctx->zh) {
120
+ rb_raise(rb_eRuntimeError, "error connecting to zookeeper: %d", errno);
121
+ }
122
+
123
+ rb_iv_set(self, "@data", data);
124
+ rb_iv_set(self, "@_running", Qtrue);
125
+
126
+ return Qnil;
127
+ }
128
+
129
+ #define FETCH_DATA_PTR(x, y) \
130
+ struct zkrb_instance_data * y; \
131
+ Data_Get_Struct(rb_iv_get(x, "@data"), struct zkrb_instance_data, y); \
132
+ if ((y)->zh == NULL) \
133
+ rb_raise(rb_eRuntimeError, "zookeeper handle is closed")
134
+
135
+ #define STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, cb_ctx, w_ctx, call_type) \
136
+ if (TYPE(reqid) != T_FIXNUM && TYPE(reqid) != T_BIGNUM) { \
137
+ rb_raise(rb_eTypeError, "reqid must be Fixnum/Bignum"); \
138
+ return Qnil; \
139
+ } \
140
+ Check_Type(path, T_STRING); \
141
+ struct zkrb_instance_data * zk; \
142
+ Data_Get_Struct(rb_iv_get(self, "@data"), struct zkrb_instance_data, zk); \
143
+ if (!zk->zh) \
144
+ rb_raise(rb_eRuntimeError, "zookeeper handle is closed"); \
145
+ zkrb_calling_context* cb_ctx = \
146
+ (async != Qfalse && async != Qnil) ? \
147
+ zkrb_calling_context_alloc(NUM2LL(reqid), zk->queue) : \
148
+ NULL; \
149
+ zkrb_calling_context* w_ctx = \
150
+ (watch != Qfalse && watch != Qnil) ? \
151
+ zkrb_calling_context_alloc(NUM2LL(reqid), zk->queue) : \
152
+ NULL; \
153
+ int a = (async != Qfalse && async != Qnil); \
154
+ int w = (watch != Qfalse && watch != Qnil); \
155
+ zkrb_call_type call_type; \
156
+ if (a) { if (w) { call_type = ASYNC_WATCH; } else { call_type = ASYNC; } } \
157
+ else { if (w) { call_type = SYNC_WATCH; } else { call_type = SYNC; } }
158
+
159
+ static VALUE method_get_children(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE watch) {
160
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
161
+
162
+ struct String_vector strings;
163
+ struct Stat stat;
164
+
165
+ int rc;
166
+ switch (call_type) {
167
+ case SYNC:
168
+ rc = zoo_get_children2(zk->zh, RSTRING_PTR(path), 0, &strings, &stat);
169
+ break;
170
+
171
+ case SYNC_WATCH:
172
+ rc = zoo_wget_children2(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, &strings, &stat);
173
+ break;
174
+
175
+ case ASYNC:
176
+ rc = zoo_aget_children2(zk->zh, RSTRING_PTR(path), 0, zkrb_strings_stat_callback, data_ctx);
177
+ break;
178
+
179
+ case ASYNC_WATCH:
180
+ rc = zoo_awget_children2(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, zkrb_strings_stat_callback, data_ctx);
181
+ break;
182
+ }
183
+
184
+ VALUE output = rb_ary_new();
185
+ rb_ary_push(output, INT2FIX(rc));
186
+ if (IS_SYNC(call_type) && rc == ZOK) {
187
+ rb_ary_push(output, zkrb_string_vector_to_ruby(&strings));
188
+ rb_ary_push(output, zkrb_stat_to_rarray(&stat));
189
+ }
190
+ return output;
191
+ }
192
+
193
+ static VALUE method_exists(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE watch) {
194
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
195
+
196
+ struct Stat stat;
197
+
198
+ int rc;
199
+ switch (call_type) {
200
+ case SYNC:
201
+ rc = zoo_exists(zk->zh, RSTRING_PTR(path), 0, &stat);
202
+ break;
203
+
204
+ case SYNC_WATCH:
205
+ rc = zoo_wexists(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, &stat);
206
+ break;
207
+
208
+ case ASYNC:
209
+ rc = zoo_aexists(zk->zh, RSTRING_PTR(path), 0, zkrb_stat_callback, data_ctx);
210
+ break;
211
+
212
+ case ASYNC_WATCH:
213
+ rc = zoo_awexists(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, zkrb_stat_callback, data_ctx);
214
+ break;
215
+ }
216
+
217
+ VALUE output = rb_ary_new();
218
+ rb_ary_push(output, INT2FIX(rc));
219
+ if (IS_SYNC(call_type) && rc == ZOK) {
220
+ rb_ary_push(output, zkrb_stat_to_rarray(&stat));
221
+ }
222
+ return output;
223
+ }
224
+
225
+ static VALUE method_create(VALUE self, VALUE reqid, VALUE path, VALUE data, VALUE async, VALUE acls, VALUE flags) {
226
+ VALUE watch = Qfalse;
227
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
228
+
229
+ if (data != Qnil) Check_Type(data, T_STRING);
230
+ Check_Type(flags, T_FIXNUM);
231
+ const char *data_ptr = (data == Qnil) ? NULL : RSTRING_PTR(data);
232
+ size_t data_len = (data == Qnil) ? -1 : RSTRING_LEN(data);
233
+
234
+ struct ACL_vector *aclptr = NULL;
235
+ if (acls != Qnil) { aclptr = zkrb_ruby_to_aclvector(acls); }
236
+ char realpath[16384];
237
+
238
+ int rc;
239
+ switch (call_type) {
240
+ case SYNC:
241
+ rc = zoo_create(zk->zh, RSTRING_PTR(path), data_ptr, data_len, aclptr, FIX2INT(flags), realpath, sizeof(realpath));
242
+ break;
243
+ case ASYNC:
244
+ rc = zoo_acreate(zk->zh, RSTRING_PTR(path), data_ptr, data_len, aclptr, FIX2INT(flags), zkrb_string_callback, data_ctx);
245
+ break;
246
+ default:
247
+ /* TODO(wickman) raise proper argument error */
248
+ return Qnil;
249
+ break;
250
+ }
251
+
252
+ if (aclptr) {
253
+ deallocate_ACL_vector(aclptr);
254
+ free(aclptr);
255
+ }
256
+
257
+ VALUE output = rb_ary_new();
258
+ rb_ary_push(output, INT2FIX(rc));
259
+ if (IS_SYNC(call_type) && rc == ZOK) {
260
+ return rb_ary_push(output, rb_str_new2(realpath));
261
+ }
262
+ return output;
263
+ }
264
+
265
+ static VALUE method_delete(VALUE self, VALUE reqid, VALUE path, VALUE version, VALUE async) {
266
+ VALUE watch = Qfalse;
267
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
268
+ Check_Type(version, T_FIXNUM);
269
+
270
+ int rc = 0;
271
+ switch (call_type) {
272
+ case SYNC:
273
+ rc = zoo_delete(zk->zh, RSTRING_PTR(path), FIX2INT(version));
274
+ break;
275
+ case ASYNC:
276
+ rc = zoo_adelete(zk->zh, RSTRING_PTR(path), FIX2INT(version), zkrb_void_callback, data_ctx);
277
+ break;
278
+ default:
279
+ /* TODO(wickman) raise proper argument error */
280
+ return Qnil;
281
+ break;
282
+ }
283
+
284
+ return INT2FIX(rc);
285
+ }
286
+
287
+ #define MAX_ZNODE_SIZE 1048576
288
+
289
+ static VALUE method_get(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE watch) {
290
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
291
+
292
+ /* ugh */
293
+ char * data = malloc(MAX_ZNODE_SIZE);
294
+ int data_len = MAX_ZNODE_SIZE;
295
+ struct Stat stat;
296
+
297
+ int rc;
298
+
299
+ switch (call_type) {
300
+ case SYNC:
301
+ rc = zoo_get(zk->zh, RSTRING_PTR(path), 0, data, &data_len, &stat);
302
+ break;
303
+
304
+ case SYNC_WATCH:
305
+ rc = zoo_wget(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, data, &data_len, &stat);
306
+ break;
307
+
308
+ case ASYNC:
309
+ rc = zoo_aget(zk->zh, RSTRING_PTR(path), 0, zkrb_data_callback, data_ctx);
310
+ break;
311
+
312
+ case ASYNC_WATCH:
313
+ rc = zoo_awget(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, zkrb_data_callback, data_ctx);
314
+ break;
315
+ }
316
+
317
+ VALUE output = rb_ary_new();
318
+ rb_ary_push(output, INT2FIX(rc));
319
+ if (IS_SYNC(call_type) && rc == ZOK) {
320
+ if (data_len == -1)
321
+ rb_ary_push(output, Qnil); /* No data associated with path */
322
+ else
323
+ rb_ary_push(output, rb_str_new(data, data_len));
324
+ rb_ary_push(output, zkrb_stat_to_rarray(&stat));
325
+ }
326
+ free(data);
327
+
328
+ return output;
329
+ }
330
+
331
+ static VALUE method_set(VALUE self, VALUE reqid, VALUE path, VALUE data, VALUE async, VALUE version) {
332
+ VALUE watch = Qfalse;
333
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
334
+
335
+ struct Stat stat;
336
+ if (data != Qnil) Check_Type(data, T_STRING);
337
+ const char *data_ptr = (data == Qnil) ? NULL : RSTRING_PTR(data);
338
+ size_t data_len = (data == Qnil) ? -1 : RSTRING_LEN(data);
339
+
340
+ int rc;
341
+ switch (call_type) {
342
+ case SYNC:
343
+ rc = zoo_set2(zk->zh, RSTRING_PTR(path), data_ptr, data_len, FIX2INT(version), &stat);
344
+ break;
345
+ case ASYNC:
346
+ rc = zoo_aset(zk->zh, RSTRING_PTR(path), data_ptr, data_len, FIX2INT(version),
347
+ zkrb_stat_callback, data_ctx);
348
+ break;
349
+ default:
350
+ /* TODO(wickman) raise proper argument error */
351
+ return Qnil;
352
+ break;
353
+ }
354
+
355
+ VALUE output = rb_ary_new();
356
+ rb_ary_push(output, INT2FIX(rc));
357
+ if (IS_SYNC(call_type) && rc == ZOK) {
358
+ rb_ary_push(output, zkrb_stat_to_rarray(&stat));
359
+ }
360
+ return output;
361
+ }
362
+
363
+ static VALUE method_set_acl(VALUE self, VALUE reqid, VALUE path, VALUE acls, VALUE async, VALUE version) {
364
+ VALUE watch = Qfalse;
365
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
366
+ struct ACL_vector * aclptr = zkrb_ruby_to_aclvector(acls);
367
+
368
+ int rc;
369
+ switch (call_type) {
370
+ case SYNC:
371
+ rc = zoo_set_acl(zk->zh, RSTRING_PTR(path), FIX2INT(version), aclptr);
372
+ break;
373
+ case ASYNC:
374
+ rc = zoo_aset_acl(zk->zh, RSTRING_PTR(path), FIX2INT(version), aclptr, zkrb_void_callback, data_ctx);
375
+ break;
376
+ default:
377
+ /* TODO(wickman) raise proper argument error */
378
+ return Qnil;
379
+ break;
380
+ }
381
+
382
+ deallocate_ACL_vector(aclptr);
383
+ free(aclptr);
384
+
385
+ return INT2FIX(rc);
386
+ }
387
+
388
+ static VALUE method_get_acl(VALUE self, VALUE reqid, VALUE path, VALUE async) {
389
+ VALUE watch = Qfalse;
390
+ STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
391
+
392
+ struct ACL_vector acls;
393
+ struct Stat stat;
394
+
395
+ int rc;
396
+ switch (call_type) {
397
+ case SYNC:
398
+ rc = zoo_get_acl(zk->zh, RSTRING_PTR(path), &acls, &stat);
399
+ break;
400
+ case ASYNC:
401
+ rc = zoo_aget_acl(zk->zh, RSTRING_PTR(path), zkrb_acl_callback, data_ctx);
402
+ break;
403
+ default:
404
+ /* TODO(wickman) raise proper argument error */
405
+ return Qnil;
406
+ break;
407
+ }
408
+
409
+ VALUE output = rb_ary_new();
410
+ rb_ary_push(output, INT2FIX(rc));
411
+ if (IS_SYNC(call_type) && rc == ZOK) {
412
+ rb_ary_push(output, zkrb_acl_vector_to_ruby(&acls));
413
+ rb_ary_push(output, zkrb_stat_to_rarray(&stat));
414
+ deallocate_ACL_vector(&acls);
415
+ }
416
+ return output;
417
+ }
418
+
419
+ static int is_running(VALUE self) {
420
+ VALUE rval = rb_iv_get(self, "@_running");
421
+ return RTEST(rval);
422
+ }
423
+
424
+ static VALUE method_get_next_event(VALUE self) {
425
+ char buf[64];
426
+ FETCH_DATA_PTR(self, zk);
427
+
428
+ for (;;) {
429
+ if (!is_running(self)) {
430
+ /* fprintf(stderr, "method_get_next_event: running is false, returning nil\n");*/
431
+ return Qnil; // this case for shutdown
432
+ }
433
+
434
+ zkrb_event_t *event = zkrb_dequeue(zk->queue, 1);
435
+
436
+ /*
437
+ * If no events found, wait for an event by using rb_thread_select() on the
438
+ * queue's pipe. Note that the ZK handle might be closed while we're
439
+ * waiting; if this happens, the rb_thread_select() will fail, and we can't
440
+ * safely touch the "zk" instance handle.
441
+ */
442
+ if (event == NULL) {
443
+ int fd = zk->queue->pipe_read;
444
+ fd_set rset;
445
+
446
+ FD_ZERO(&rset);
447
+ FD_SET(fd, &rset);
448
+
449
+ if (rb_thread_select(fd + 1, &rset, NULL, NULL, NULL) == -1)
450
+ rb_raise(rb_eRuntimeError, "select failed: %d", errno);
451
+
452
+ if (read(fd, buf, sizeof(buf)) == -1)
453
+ rb_raise(rb_eRuntimeError, "read failed: %d", errno);
454
+
455
+ continue;
456
+ }
457
+
458
+ VALUE hash = zkrb_event_to_ruby(event);
459
+ zkrb_event_free(event);
460
+ return hash;
461
+ }
462
+ }
463
+
464
+ static VALUE method_has_events(VALUE self) {
465
+ VALUE rb_event;
466
+ FETCH_DATA_PTR(self, zk);
467
+
468
+ rb_event = zkrb_peek(zk->queue) != NULL ? Qtrue : Qfalse;
469
+ return rb_event;
470
+ }
471
+
472
+ static VALUE method_client_id(VALUE self) {
473
+ FETCH_DATA_PTR(self, zk);
474
+ const clientid_t *id = zoo_client_id(zk->zh);
475
+ return UINT2NUM(id->client_id);
476
+ }
477
+
478
+ // wake up the event loop, used when shutting down
479
+ static VALUE method_wake_event_loop_bang(VALUE self) {
480
+ FETCH_DATA_PTR(self, zk);
481
+
482
+ ssize_t ret = write(zk->queue->pipe_write, "0", 1); /* Wake up Ruby listener */
483
+
484
+ if (ret == -1)
485
+ rb_raise(rb_eRuntimeError, "write to pipe failed: %d", errno);
486
+
487
+ return Qnil;
488
+ };
489
+
490
+ static VALUE method_close(VALUE self) {
491
+ FETCH_DATA_PTR(self, zk);
492
+
493
+ /* Note that after zookeeper_close() returns, ZK handle is invalid */
494
+ int rc = destroy_zkrb_instance(zk);
495
+ return INT2FIX(rc);
496
+ }
497
+
498
+ static VALUE method_deterministic_conn_order(VALUE self, VALUE yn) {
499
+ zoo_deterministic_conn_order(yn == Qtrue);
500
+ return Qnil;
501
+ }
502
+
503
+ static VALUE method_is_unrecoverable(VALUE self) {
504
+ FETCH_DATA_PTR(self, zk);
505
+ return is_unrecoverable(zk->zh) == ZINVALIDSTATE ? Qtrue : Qfalse;
506
+ }
507
+
508
+ static VALUE method_state(VALUE self) {
509
+ FETCH_DATA_PTR(self, zk);
510
+ return INT2NUM(zoo_state(zk->zh));
511
+ }
512
+
513
+ static VALUE method_recv_timeout(VALUE self) {
514
+ FETCH_DATA_PTR(self, zk);
515
+ return INT2NUM(zoo_recv_timeout(zk->zh));
516
+ }
517
+
518
+ // how do you make a class method??
519
+ static VALUE method_set_debug_level(VALUE self, VALUE level) {
520
+ Check_Type(level, T_FIXNUM);
521
+ ZKRBDebugging = (FIX2INT(level) == ZOO_LOG_LEVEL_DEBUG);
522
+ zoo_set_debug_level(FIX2INT(level));
523
+ return Qnil;
524
+ }
525
+
526
+ static VALUE method_zerror(VALUE self, VALUE errc) {
527
+ return rb_str_new2(zerror(FIX2INT(errc)));
528
+ }
529
+
530
+ static void zkrb_define_methods(void) {
531
+ #define DEFINE_METHOD(method, args) { \
532
+ rb_define_method(Zookeeper, #method, method_ ## method, args); }
533
+ #define DEFINE_CLASS_METHOD(method, args) { \
534
+ rb_define_singleton_method(Zookeeper, #method, method_ ## method, args); }
535
+
536
+ DEFINE_METHOD(init, -1);
537
+ DEFINE_METHOD(get_children, 4);
538
+ DEFINE_METHOD(exists, 4);
539
+ DEFINE_METHOD(create, 6);
540
+ DEFINE_METHOD(delete, 4);
541
+ DEFINE_METHOD(get, 4);
542
+ DEFINE_METHOD(set, 5);
543
+ DEFINE_METHOD(set_acl, 5);
544
+ DEFINE_METHOD(get_acl, 3);
545
+ DEFINE_METHOD(client_id, 0);
546
+ DEFINE_METHOD(close, 0);
547
+ DEFINE_METHOD(deterministic_conn_order, 1);
548
+ DEFINE_METHOD(is_unrecoverable, 0);
549
+ DEFINE_METHOD(recv_timeout, 1);
550
+ DEFINE_METHOD(state, 0);
551
+ // TODO
552
+ // DEFINE_METHOD(add_auth, 3);
553
+ // DEFINE_METHOD(async, 1);
554
+
555
+ // methods for the ruby-side event manager
556
+ DEFINE_METHOD(get_next_event, 0);
557
+ DEFINE_METHOD(has_events, 0);
558
+
559
+ // Make these class methods?
560
+ DEFINE_METHOD(set_debug_level, 1);
561
+ DEFINE_METHOD(zerror, 1);
562
+
563
+ rb_define_method(Zookeeper, "wake_event_loop!", method_wake_event_loop_bang, 0);
564
+ }
565
+
566
+ void Init_zookeeper_c() {
567
+ ZKRBDebugging = 0;
568
+ /* initialize Zookeeper class */
569
+ Zookeeper = rb_define_class("CZookeeper", rb_cObject);
570
+ zkrb_define_methods();
571
+ }