zookeeper 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,5 +1,6 @@
1
- v0.4.3 Fix a handful of memory-related bugs, fix SIGSEGV on master change,
2
- reduce latency of event handling, fix compilation on OSX.
1
+ v0.4.4 Fix race condition on close, possible data corruption on async get.
2
+
3
+ v0.4.3 Fix a handful of memory-related bugs, fix SIGSEGV on master change, reduce latency of event handling, fix compilation on OSX.
3
4
 
4
5
  v0.4.2 Add options to Zookeeper#initialize, silence most Zookeeper logs.
5
6
 
data/Rakefile CHANGED
@@ -2,10 +2,8 @@ require 'echoe'
2
2
 
3
3
  Echoe.new("zookeeper") do |p|
4
4
  p.author = "Phillip Pearson, Eric Maland, Evan Weaver, Brian Wickman"
5
- p.project = "fauna"
5
+ p.project = "twitter"
6
6
  p.summary = "An interface to the Zookeeper distributed configuration server."
7
- p.url = "https://github.com/twitter/zookeeper"
8
- p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
9
7
  p.clean_pattern += ["ext/lib", "ext/include", "ext/c", "ext/bin", "ext/conftest.dSYM"]
10
8
  p.rdoc_pattern = /README|TODO|LICENSE|CHANGELOG|BENCH|COMPAT|zookeeper_c.c|zookeeper.rb/
11
- end
9
+ end
data/ext/zookeeper_c.c CHANGED
@@ -26,6 +26,7 @@ struct zkrb_instance_data {
26
26
  zhandle_t *zh;
27
27
  clientid_t myid;
28
28
  zkrb_queue_t *queue;
29
+ int pending_close;
29
30
  };
30
31
 
31
32
  typedef enum {
@@ -99,13 +100,14 @@ static VALUE method_init(int argc, VALUE* argv, VALUE self) {
99
100
  free_zkrb_instance_data,
100
101
  zk_local_ctx);
101
102
  zk_local_ctx->queue = zkrb_queue_alloc();
103
+ zk_local_ctx->pending_close = 0;
102
104
 
103
105
  zoo_deterministic_conn_order(0);
104
106
 
105
107
  zkrb_calling_context *ctx =
106
108
  zkrb_calling_context_alloc(ZKRB_GLOBAL_REQ, zk_local_ctx->queue);
107
109
 
108
- zk_local_ctx->zh =
110
+ zk_local_ctx->zh =
109
111
  zookeeper_init(
110
112
  RSTRING_PTR(hostPort),
111
113
  zkrb_state_callback,
@@ -113,7 +115,7 @@ static VALUE method_init(int argc, VALUE* argv, VALUE self) {
113
115
  &zk_local_ctx->myid,
114
116
  ctx,
115
117
  0);
116
-
118
+
117
119
  #warning [wickman] TODO handle this properly on the Ruby side rather than C side
118
120
  if (!zk_local_ctx->zh) {
119
121
  rb_raise(rb_eRuntimeError, "error connecting to zookeeper: %d", errno);
@@ -156,10 +158,10 @@ static VALUE method_init(int argc, VALUE* argv, VALUE self) {
156
158
 
157
159
  static VALUE method_get_children(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE watch) {
158
160
  STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
159
-
161
+
160
162
  struct String_vector strings;
161
163
  struct Stat stat;
162
-
164
+
163
165
  int rc;
164
166
  switch (call_type) {
165
167
  case SYNC:
@@ -169,11 +171,11 @@ static VALUE method_get_children(VALUE self, VALUE reqid, VALUE path, VALUE asyn
169
171
  case SYNC_WATCH:
170
172
  rc = zoo_wget_children2(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, &strings, &stat);
171
173
  break;
172
-
174
+
173
175
  case ASYNC:
174
176
  rc = zoo_aget_children2(zk->zh, RSTRING_PTR(path), 0, zkrb_strings_stat_callback, data_ctx);
175
177
  break;
176
-
178
+
177
179
  case ASYNC_WATCH:
178
180
  rc = zoo_awget_children2(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, zkrb_strings_stat_callback, data_ctx);
179
181
  break;
@@ -190,7 +192,7 @@ static VALUE method_get_children(VALUE self, VALUE reqid, VALUE path, VALUE asyn
190
192
 
191
193
  static VALUE method_exists(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE watch) {
192
194
  STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
193
-
195
+
194
196
  struct Stat stat;
195
197
 
196
198
  int rc;
@@ -202,11 +204,11 @@ static VALUE method_exists(VALUE self, VALUE reqid, VALUE path, VALUE async, VAL
202
204
  case SYNC_WATCH:
203
205
  rc = zoo_wexists(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, &stat);
204
206
  break;
205
-
207
+
206
208
  case ASYNC:
207
209
  rc = zoo_aexists(zk->zh, RSTRING_PTR(path), 0, zkrb_stat_callback, data_ctx);
208
210
  break;
209
-
211
+
210
212
  case ASYNC_WATCH:
211
213
  rc = zoo_awexists(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, zkrb_stat_callback, data_ctx);
212
214
  break;
@@ -223,12 +225,12 @@ static VALUE method_exists(VALUE self, VALUE reqid, VALUE path, VALUE async, VAL
223
225
  static VALUE method_create(VALUE self, VALUE reqid, VALUE path, VALUE data, VALUE async, VALUE acls, VALUE flags) {
224
226
  VALUE watch = Qfalse;
225
227
  STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
226
-
228
+
227
229
  if (data != Qnil) Check_Type(data, T_STRING);
228
230
  Check_Type(flags, T_FIXNUM);
229
231
  const char *data_ptr = (data == Qnil) ? NULL : RSTRING_PTR(data);
230
232
  size_t data_len = (data == Qnil) ? -1 : RSTRING_LEN(data);
231
-
233
+
232
234
  struct ACL_vector *aclptr = NULL;
233
235
  if (acls != Qnil) { aclptr = zkrb_ruby_to_aclvector(acls); }
234
236
  char realpath[16384];
@@ -261,10 +263,10 @@ static VALUE method_create(VALUE self, VALUE reqid, VALUE path, VALUE data, VALU
261
263
  }
262
264
 
263
265
  static VALUE method_delete(VALUE self, VALUE reqid, VALUE path, VALUE version, VALUE async) {
264
- VALUE watch = Qfalse;
266
+ VALUE watch = Qfalse;
265
267
  STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
266
268
  Check_Type(version, T_FIXNUM);
267
-
269
+
268
270
  int rc = 0;
269
271
  switch (call_type) {
270
272
  case SYNC:
@@ -293,7 +295,7 @@ static VALUE method_get(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE
293
295
  struct Stat stat;
294
296
 
295
297
  int rc;
296
-
298
+
297
299
  switch (call_type) {
298
300
  case SYNC:
299
301
  rc = zoo_get(zk->zh, RSTRING_PTR(path), 0, data, &data_len, &stat);
@@ -302,11 +304,11 @@ static VALUE method_get(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE
302
304
  case SYNC_WATCH:
303
305
  rc = zoo_wget(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, data, &data_len, &stat);
304
306
  break;
305
-
307
+
306
308
  case ASYNC:
307
309
  rc = zoo_aget(zk->zh, RSTRING_PTR(path), 0, zkrb_data_callback, data_ctx);
308
310
  break;
309
-
311
+
310
312
  case ASYNC_WATCH:
311
313
  rc = zoo_awget(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, zkrb_data_callback, data_ctx);
312
314
  break;
@@ -329,7 +331,7 @@ static VALUE method_get(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE
329
331
  static VALUE method_set(VALUE self, VALUE reqid, VALUE path, VALUE data, VALUE async, VALUE version) {
330
332
  VALUE watch = Qfalse;
331
333
  STANDARD_PREAMBLE(self, zk, reqid, path, async, watch, data_ctx, watch_ctx, call_type);
332
-
334
+
333
335
  struct Stat stat;
334
336
  if (data != Qnil) Check_Type(data, T_STRING);
335
337
  const char *data_ptr = (data == Qnil) ? NULL : RSTRING_PTR(data);
@@ -389,7 +391,7 @@ static VALUE method_get_acl(VALUE self, VALUE reqid, VALUE path, VALUE async) {
389
391
 
390
392
  struct ACL_vector acls;
391
393
  struct Stat stat;
392
-
394
+
393
395
  int rc;
394
396
  switch (call_type) {
395
397
  case SYNC:
@@ -421,12 +423,7 @@ static VALUE method_get_next_event(VALUE self) {
421
423
  for (;;) {
422
424
  zkrb_event_t *event = zkrb_dequeue(zk->queue, 1);
423
425
 
424
- /*
425
- * If no events found, wait for an event by using rb_thread_select() on the
426
- * queue's pipe. Note that the ZK handle might be closed while we're
427
- * waiting; if this happens, the rb_thread_select() will fail, and we can't
428
- * safely touch the "zk" instance handle.
429
- */
426
+ /* Wait for an event using rb_thread_select() on the queue's pipe */
430
427
  if (event == NULL) {
431
428
  int fd = zk->queue->pipe_read;
432
429
  fd_set rset;
@@ -440,6 +437,9 @@ static VALUE method_get_next_event(VALUE self) {
440
437
  if (read(fd, buf, sizeof(buf)) == -1)
441
438
  rb_raise(rb_eRuntimeError, "read failed: %d", errno);
442
439
 
440
+ if (zk->pending_close)
441
+ return Qnil;
442
+
443
443
  continue;
444
444
  }
445
445
 
@@ -463,6 +463,15 @@ static VALUE method_client_id(VALUE self) {
463
463
  return UINT2NUM(id->client_id);
464
464
  }
465
465
 
466
+ static VALUE method_signal_pending_close(VALUE self) {
467
+ FETCH_DATA_PTR(self, zk);
468
+
469
+ zk->pending_close = 1;
470
+ zkrb_signal(zk->queue);
471
+
472
+ return Qnil;
473
+ }
474
+
466
475
  static VALUE method_close(VALUE self) {
467
476
  FETCH_DATA_PTR(self, zk);
468
477
 
@@ -517,8 +526,9 @@ static void zkrb_define_methods(void) {
517
526
  DEFINE_METHOD(get, 4);
518
527
  DEFINE_METHOD(set, 5);
519
528
  DEFINE_METHOD(set_acl, 5);
520
- DEFINE_METHOD(get_acl, 3);
529
+ DEFINE_METHOD(get_acl, 3);
521
530
  DEFINE_METHOD(client_id, 0);
531
+ DEFINE_METHOD(signal_pending_close, 0);
522
532
  DEFINE_METHOD(close, 0);
523
533
  DEFINE_METHOD(deterministic_conn_order, 1);
524
534
  DEFINE_METHOD(is_unrecoverable, 0);
@@ -528,11 +538,11 @@ static void zkrb_define_methods(void) {
528
538
  // DEFINE_METHOD(add_auth, 3);
529
539
  // DEFINE_METHOD(async, 1);
530
540
 
531
- // methods for the ruby-side event manager
541
+ // methods for the ruby-side event manager
532
542
  DEFINE_METHOD(get_next_event, 0);
533
543
  DEFINE_METHOD(has_events, 0);
534
544
 
535
- // Make these class methods?
545
+ // Make these class methods?
536
546
  DEFINE_METHOD(set_debug_level, 1);
537
547
  DEFINE_METHOD(zerror, 1);
538
548
  }
data/ext/zookeeper_lib.c CHANGED
@@ -69,6 +69,14 @@ zkrb_event_t* zkrb_dequeue(zkrb_queue_t *q, int need_lock) {
69
69
  }
70
70
  }
71
71
 
72
+ void zkrb_signal(zkrb_queue_t *q) {
73
+ pthread_mutex_lock(&zkrb_q_mutex);
74
+ ssize_t ret = write(q->pipe_write, "0", 1); /* Wake up Ruby listener */
75
+ pthread_mutex_unlock(&zkrb_q_mutex);
76
+ if (ret == -1)
77
+ rb_raise(rb_eRuntimeError, "write to pipe failed: %d", errno);
78
+ }
79
+
72
80
  zkrb_queue_t *zkrb_queue_alloc(void) {
73
81
  int pfd[2];
74
82
  if (pipe(pfd) == -1)
@@ -187,14 +195,14 @@ VALUE zkrb_event_to_ruby(zkrb_event_t *event) {
187
195
  case ZKRB_DATA: {
188
196
  struct zkrb_data_completion *data_ctx = event->completion.data_completion;
189
197
  if (ZKRBDebugging) zkrb_print_stat(data_ctx->stat);
190
- rb_hash_aset(hash, GET_SYM("data"), data_ctx->data ? rb_str_new2(data_ctx->data) : Qnil);
198
+ rb_hash_aset(hash, GET_SYM("data"), data_ctx->data ? rb_str_new(data_ctx->data, data_ctx->data_len) : Qnil);
191
199
  rb_hash_aset(hash, GET_SYM("stat"), data_ctx->stat ? zkrb_stat_to_rarray(data_ctx->stat) : Qnil);
192
200
  break;
193
201
  }
194
202
  case ZKRB_STAT: {
195
203
  struct zkrb_stat_completion *stat_ctx = event->completion.stat_completion;
196
204
  rb_hash_aset(hash, GET_SYM("stat"), stat_ctx->stat ? zkrb_stat_to_rarray(stat_ctx->stat) : Qnil);
197
- break;
205
+ break;
198
206
  }
199
207
  case ZKRB_STRING: {
200
208
  struct zkrb_string_completion *string_ctx = event->completion.string_completion;
@@ -328,14 +336,21 @@ void zkrb_data_callback(
328
336
  struct zkrb_data_completion *dc = malloc(sizeof(struct zkrb_data_completion));
329
337
  dc->data = NULL;
330
338
  dc->stat = NULL;
331
- if (value != NULL) { dc->data = malloc(value_len); memcpy(dc->data, value, value_len); }
339
+ dc->data_len = 0;
340
+
341
+ if (value != NULL) {
342
+ dc->data = malloc(value_len);
343
+ dc->data_len = value_len;
344
+ memcpy(dc->data, value, value_len);
345
+ }
346
+
332
347
  if (stat != NULL) { dc->stat = malloc(sizeof(struct Stat)); memcpy(dc->stat, stat, sizeof(struct Stat)); }
333
348
 
334
349
  ZKH_SETUP_EVENT(queue, event);
335
350
  event->rc = rc;
336
351
  event->type = ZKRB_DATA;
337
352
  event->completion.data_completion = dc;
338
-
353
+
339
354
  zkrb_enqueue(queue, event);
340
355
  }
341
356
 
@@ -354,7 +369,7 @@ void zkrb_stat_callback(
354
369
  event->rc = rc;
355
370
  event->type = ZKRB_STAT;
356
371
  event->completion.stat_completion = sc;
357
-
372
+
358
373
  zkrb_enqueue(queue, event);
359
374
  }
360
375
 
@@ -374,7 +389,7 @@ void zkrb_string_callback(
374
389
  event->rc = rc;
375
390
  event->type = ZKRB_STRING;
376
391
  event->completion.string_completion = sc;
377
-
392
+
378
393
  zkrb_enqueue(queue, event);
379
394
  }
380
395
 
@@ -393,7 +408,7 @@ void zkrb_strings_callback(
393
408
  event->rc = rc;
394
409
  event->type = ZKRB_STRINGS;
395
410
  event->completion.strings_completion = sc;
396
-
411
+
397
412
  zkrb_enqueue(queue, event);
398
413
  }
399
414
 
@@ -413,7 +428,7 @@ void zkrb_strings_stat_callback(
413
428
  event->rc = rc;
414
429
  event->type = ZKRB_STRINGS_STAT;
415
430
  event->completion.strings_stat_completion = sc;
416
-
431
+
417
432
  zkrb_enqueue(queue, event);
418
433
  }
419
434
 
@@ -428,7 +443,7 @@ void zkrb_void_callback(
428
443
  event->rc = rc;
429
444
  event->type = ZKRB_VOID;
430
445
  event->completion.void_completion = NULL;
431
-
446
+
432
447
  zkrb_enqueue(queue, event);
433
448
  }
434
449
 
@@ -449,7 +464,7 @@ void zkrb_acl_callback(
449
464
  event->rc = rc;
450
465
  event->type = ZKRB_ACL;
451
466
  event->completion.acl_completion = ac;
452
-
467
+
453
468
  /* should be synchronized */
454
469
  zkrb_enqueue(queue, event);
455
470
  }
@@ -487,22 +502,22 @@ struct ACL_vector * zkrb_ruby_to_aclvector(VALUE acl_ary) {
487
502
  #warning [wickman] TODO test zkrb_ruby_to_aclvector
488
503
  struct ACL zkrb_ruby_to_acl(VALUE rubyacl) {
489
504
  struct ACL acl;
490
-
505
+
491
506
  VALUE perms = rb_iv_get(rubyacl, "@perms");
492
507
  VALUE rubyid = rb_iv_get(rubyacl, "@id");
493
508
  acl.perms = NUM2INT(perms);
494
509
  acl.id = zkrb_ruby_to_id(rubyid);
495
-
510
+
496
511
  return acl;
497
512
  }
498
513
 
499
514
  #warning [wickman] TODO zkrb_ruby_to_id error checking? test
500
515
  struct Id zkrb_ruby_to_id(VALUE rubyid) {
501
516
  struct Id id;
502
-
517
+
503
518
  VALUE scheme = rb_iv_get(rubyid, "@scheme");
504
519
  VALUE ident = rb_iv_get(rubyid, "@id");
505
-
520
+
506
521
  if (scheme != Qnil) {
507
522
  id.scheme = malloc(RSTRING_LEN(scheme) + 1);
508
523
  strncpy(id.scheme, RSTRING_PTR(scheme), RSTRING_LEN(scheme));
@@ -518,7 +533,7 @@ struct Id zkrb_ruby_to_id(VALUE rubyid) {
518
533
  } else {
519
534
  id.id = NULL;
520
535
  }
521
-
536
+
522
537
  return id;
523
538
  }
524
539
 
@@ -542,17 +557,17 @@ VALUE zkrb_string_vector_to_ruby(struct String_vector *string_vector) {
542
557
 
543
558
  VALUE zkrb_stat_to_rarray(const struct Stat* stat) {
544
559
  return rb_ary_new3(11,
545
- LL2NUM(stat->czxid),
546
- LL2NUM(stat->mzxid),
547
- LL2NUM(stat->ctime),
548
- LL2NUM(stat->mtime),
549
- INT2NUM(stat->version),
550
- INT2NUM(stat->cversion),
551
- INT2NUM(stat->aversion),
552
- LL2NUM(stat->ephemeralOwner),
553
- INT2NUM(stat->dataLength),
554
- INT2NUM(stat->numChildren),
555
- LL2NUM(stat->pzxid));
560
+ LL2NUM(stat->czxid),
561
+ LL2NUM(stat->mzxid),
562
+ LL2NUM(stat->ctime),
563
+ LL2NUM(stat->mtime),
564
+ INT2NUM(stat->version),
565
+ INT2NUM(stat->cversion),
566
+ INT2NUM(stat->aversion),
567
+ LL2NUM(stat->ephemeralOwner),
568
+ INT2NUM(stat->dataLength),
569
+ INT2NUM(stat->numChildren),
570
+ LL2NUM(stat->pzxid));
556
571
  }
557
572
 
558
573
  VALUE zkrb_stat_to_rhash(const struct Stat *stat) {
data/ext/zookeeper_lib.h CHANGED
@@ -26,6 +26,7 @@ extern pthread_mutex_t zkrb_q_mutex;
26
26
 
27
27
  struct zkrb_data_completion {
28
28
  char *data;
29
+ int data_len;
29
30
  struct Stat *stat;
30
31
  };
31
32
 
@@ -108,6 +109,7 @@ void zkrb_event_free(zkrb_event_t *ptr);
108
109
  void zkrb_enqueue(zkrb_queue_t *queue, zkrb_event_t *elt);
109
110
  zkrb_event_t * zkrb_peek(zkrb_queue_t *queue);
110
111
  zkrb_event_t * zkrb_dequeue(zkrb_queue_t *queue, int need_lock);
112
+ void zkrb_signal(zkrb_queue_t *queue);
111
113
 
112
114
  void zkrb_print_stat(const struct Stat *s);
113
115
 
data/lib/zookeeper.rb CHANGED
@@ -22,7 +22,7 @@ class Zookeeper < CZookeeper
22
22
  ZOO_LOG_LEVEL_WARN = 2
23
23
  ZOO_LOG_LEVEL_INFO = 3
24
24
  ZOO_LOG_LEVEL_DEBUG = 4
25
-
25
+
26
26
  def reopen(timeout = 10)
27
27
  init(@host)
28
28
  if timeout > 0
@@ -34,6 +34,7 @@ class Zookeeper < CZookeeper
34
34
  end
35
35
  # flushes all outstanding watcher reqs.
36
36
  @watcher_reqs = { ZKRB_GLOBAL_CB_REQ => { :watcher => get_default_global_watcher } }
37
+ setup_dispatch_thread!
37
38
  state
38
39
  end
39
40
 
@@ -44,7 +45,6 @@ class Zookeeper < CZookeeper
44
45
  @current_req_id = 1
45
46
  @host = host
46
47
  return nil if reopen(timeout) != Zookeeper::ZOO_CONNECTED_STATE
47
- setup_dispatch_thread!
48
48
  end
49
49
 
50
50
  public
@@ -52,14 +52,14 @@ public
52
52
  assert_open
53
53
  assert_supported_keys(options, [:path, :watcher, :watcher_context, :callback, :callback_context])
54
54
  assert_required_keys(options, [:path])
55
-
55
+
56
56
  req_id = setup_call(options)
57
57
  rc, value, stat = super(req_id, options[:path], options[:callback], options[:watcher])
58
58
 
59
59
  rv = { :req_id => req_id, :rc => rc }
60
60
  options[:callback] ? rv : rv.merge(:data => value, :stat => Stat.new(stat))
61
61
  end
62
-
62
+
63
63
  def set(options = {})
64
64
  assert_open
65
65
  assert_supported_keys(options, [:path, :data, :version, :callback, :callback_context])
@@ -72,7 +72,7 @@ public
72
72
  rv = { :req_id => req_id, :rc => rc }
73
73
  options[:callback] ? rv : rv.merge(:stat => Stat.new(stat))
74
74
  end
75
-
75
+
76
76
  def get_children(options = {})
77
77
  assert_open
78
78
  assert_supported_keys(options, [:path, :callback, :callback_context, :watcher, :watcher_context])
@@ -96,34 +96,34 @@ public
96
96
  rv = { :req_id => req_id, :rc => rc }
97
97
  options[:callback] ? rv : rv.merge(:stat => Stat.new(stat))
98
98
  end
99
-
99
+
100
100
  def create(options = {})
101
101
  assert_open
102
102
  assert_supported_keys(options, [:path, :data, :acl, :ephemeral, :sequence, :callback, :callback_context])
103
103
  assert_required_keys(options, [:path])
104
-
104
+
105
105
  flags = 0
106
106
  flags |= ZOO_EPHEMERAL if options[:ephemeral]
107
107
  flags |= ZOO_SEQUENCE if options[:sequence]
108
108
 
109
109
  options[:acl] ||= ZOO_OPEN_ACL_UNSAFE
110
-
110
+
111
111
  req_id = setup_call(options)
112
112
  rc, newpath = super(req_id, options[:path], options[:data], options[:callback], options[:acl], flags)
113
-
113
+
114
114
  rv = { :req_id => req_id, :rc => rc }
115
- options[:callback] ? rv : rv.merge(:path => newpath)
115
+ options[:callback] ? rv : rv.merge(:path => newpath)
116
116
  end
117
-
117
+
118
118
  def delete(options = {})
119
119
  assert_open
120
120
  assert_supported_keys(options, [:path, :version, :callback, :callback_context])
121
121
  assert_required_keys(options, [:path])
122
122
  options[:version] ||= -1
123
-
123
+
124
124
  req_id = setup_call(options)
125
125
  rc = super(req_id, options[:path], options[:version], options[:callback])
126
-
126
+
127
127
  { :req_id => req_id, :rc => rc }
128
128
  end
129
129
 
@@ -132,46 +132,55 @@ public
132
132
  assert_supported_keys(options, [:path, :acl, :version, :callback, :callback_context])
133
133
  assert_required_keys(options, [:path, :acl])
134
134
  options[:version] ||= -1
135
-
135
+
136
136
  req_id = setup_call(options)
137
137
  rc = super(req_id, options[:path], options[:acl], options[:callback], options[:version])
138
-
138
+
139
139
  { :req_id => req_id, :rc => rc }
140
140
  end
141
-
141
+
142
142
  def get_acl(options = {})
143
143
  assert_open
144
144
  assert_supported_keys(options, [:path, :callback, :callback_context])
145
145
  assert_required_keys(options, [:path])
146
-
146
+
147
147
  req_id = setup_call(options)
148
148
  rc, acls, stat = super(req_id, options[:path], options[:callback])
149
-
149
+
150
150
  rv = { :req_id => req_id, :rc => rc }
151
151
  options[:callback] ? rv : rv.merge(:acl => acls, :stat => Stat.new(stat))
152
152
  end
153
153
 
154
+ # To close a Zk handle, first shutdown the dispatcher thread; this is done by
155
+ # signalling the waiting thread that there is a pending close. We then release
156
+ # the C-land Zk state.
157
+ def close
158
+ signal_pending_close
159
+ @dispatcher.join
160
+ super
161
+ end
162
+
154
163
  private
155
164
  def setup_dispatch_thread!
156
165
  @dispatcher = Thread.new {
157
166
  while true do
158
- dispatch_next_callback
167
+ hash = get_next_event
168
+ break if hash.nil? # Pending close => exit dispatcher thread
169
+ dispatch_event(hash)
159
170
  end
160
171
  }
161
172
  end
162
173
 
163
- def dispatch_next_callback
164
- hash = get_next_event
165
-
174
+ def dispatch_event(hash)
166
175
  is_completion = hash.has_key?(:rc)
167
-
176
+
168
177
  hash[:stat] = Stat.new(hash[:stat]) if hash.has_key?(:stat)
169
178
  hash[:acl] = hash[:acl].map { |acl| ACL.new(acl) } if hash[:acl]
170
-
179
+
171
180
  callback_context = is_completion ? get_completion(hash[:req_id]) : get_watcher(hash[:req_id])
172
181
  callback = is_completion ? callback_context[:callback] : callback_context[:watcher]
173
182
  hash[:context] = callback_context[:context]
174
-
183
+
175
184
  # TODO: Eventually enforce derivation from Zookeeper::Callback
176
185
  if callback.respond_to?(:call)
177
186
  callback.call(hash)
@@ -179,7 +188,7 @@ private
179
188
  # puts "dispatch_next_callback found non-callback => #{callback.inspect}"
180
189
  end
181
190
  end
182
-
191
+
183
192
  def setup_call(opts)
184
193
  req_id = nil
185
194
  @req_mutex.synchronize {
@@ -190,7 +199,7 @@ private
190
199
  }
191
200
  req_id
192
201
  end
193
-
202
+
194
203
  def setup_watcher(req_id, call_opts)
195
204
  @watcher_reqs[req_id] = { :watcher => call_opts[:watcher],
196
205
  :context => call_opts[:watcher_context] }
@@ -200,13 +209,13 @@ private
200
209
  @completion_reqs[req_id] = { :callback => call_opts[:callback],
201
210
  :context => call_opts[:callback_context] }
202
211
  end
203
-
212
+
204
213
  def get_watcher(req_id)
205
214
  @req_mutex.synchronize {
206
215
  req_id != ZKRB_GLOBAL_CB_REQ ? @watcher_reqs.delete(req_id) : @watcher_reqs[req_id]
207
216
  }
208
217
  end
209
-
218
+
210
219
  def get_completion(req_id)
211
220
  @req_mutex.synchronize { @completion_reqs.delete(req_id) }
212
221
  end
data/zookeeper.gemspec CHANGED
@@ -2,21 +2,21 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{zookeeper}
5
- s.version = "0.4.3"
5
+ s.version = "0.4.4"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Phillip Pearson, Eric Maland, Evan Weaver, Brian Wickman"]
9
- s.date = %q{2011-02-09}
9
+ s.date = %q{2011-03-30}
10
10
  s.description = %q{An interface to the Zookeeper distributed configuration server.}
11
11
  s.email = %q{}
12
12
  s.extensions = ["ext/extconf.rb"]
13
13
  s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "ext/zookeeper_c.c", "lib/zookeeper.rb"]
14
14
  s.files = ["CHANGELOG", "LICENSE", "README", "Rakefile", "examples/cloud_config.rb", "ext/extconf.rb", "ext/zkc-3.3.2.tar.gz", "ext/zookeeper_c.c", "ext/zookeeper_lib.c", "ext/zookeeper_lib.h", "lib/zookeeper.rb", "lib/zookeeper/acls.rb", "lib/zookeeper/callbacks.rb", "lib/zookeeper/constants.rb", "lib/zookeeper/exceptions.rb", "lib/zookeeper/stat.rb", "test/test_basic.rb", "test/test_callback1.rb", "test/test_close.rb", "test/test_esoteric.rb", "test/test_watcher1.rb", "test/test_watcher2.rb", "Manifest", "zookeeper.gemspec"]
15
- s.homepage = %q{https://github.com/twitter/zookeeper}
15
+ s.homepage = %q{http://twitter.github.com/twitter/zookeeper/}
16
16
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Zookeeper", "--main", "README"]
17
17
  s.require_paths = ["lib", "ext"]
18
- s.rubyforge_project = %q{fauna}
19
- s.rubygems_version = %q{1.5.0}
18
+ s.rubyforge_project = %q{twitter}
19
+ s.rubygems_version = %q{1.6.1}
20
20
  s.summary = %q{An interface to the Zookeeper distributed configuration server.}
21
21
  s.test_files = ["test/test_basic.rb", "test/test_callback1.rb", "test/test_close.rb", "test/test_esoteric.rb", "test/test_watcher1.rb", "test/test_watcher2.rb"]
22
22
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zookeeper
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 4
9
- - 3
10
- version: 0.4.3
9
+ - 4
10
+ version: 0.4.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Phillip Pearson, Eric Maland, Evan Weaver, Brian Wickman
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-09 00:00:00 -08:00
18
+ date: 2011-03-30 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -57,7 +57,7 @@ files:
57
57
  - Manifest
58
58
  - zookeeper.gemspec
59
59
  has_rdoc: true
60
- homepage: https://github.com/twitter/zookeeper
60
+ homepage: http://twitter.github.com/twitter/zookeeper/
61
61
  licenses: []
62
62
 
63
63
  post_install_message:
@@ -92,8 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
92
  version: "1.2"
93
93
  requirements: []
94
94
 
95
- rubyforge_project: fauna
96
- rubygems_version: 1.5.0
95
+ rubyforge_project: twitter
96
+ rubygems_version: 1.6.1
97
97
  signing_key:
98
98
  specification_version: 3
99
99
  summary: An interface to the Zookeeper distributed configuration server.