zookeeper 1.2.4-java → 1.2.5-java

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.
data/CHANGELOG CHANGED
@@ -1,3 +1,24 @@
1
+ v1.2.5 cleanup locking in ZookeeperBase
2
+
3
+ * There were several situations where we would hold the lock before calling
4
+ a method on CZookeeper (inquisitors and #create in particular). This
5
+ exposed us to deadlocks in situations where an async event would be
6
+ delivered (probably a SESSION_EXPIRED event), but the callback block could
7
+ not be called because the dispatch thread would block on the mutex being
8
+ held by the caller of create.
9
+
10
+ This version cleans up that usage, and ensures that the only time we hold
11
+ the mutex is during startup/shutdown (when the value of @czk may be changing),
12
+ and in all other cases we grab the mutex, dereference, and unlock then perform
13
+ whatever action on the reference.
14
+
15
+ * Add a safety net to Continuation (which will soon be called 'Promise' or
16
+ 'Future' at the request of @eric). If any operation takes more than 30s
17
+ an exception will be raised in the calling thread. The session timeout
18
+ setting makes it so that no operation should take more than 20s, so we know
19
+ if we haven't received a reply in *longer* than that, something has gone
20
+ awry.
21
+
1
22
  v1.2.4 fix buffer overflow in CZookeeper client_id code
2
23
 
3
24
  * the 'passwd' part of the struct is a char[16], but isn't null terminated.
data/ext/c_zookeeper.rb CHANGED
@@ -176,6 +176,7 @@ class CZookeeper
176
176
  return false unless wait_until_running(timeout)
177
177
 
178
178
  @mutex.synchronize do
179
+ # TODO: use deadline here
179
180
  @state_cond.wait(timeout) unless (@state == ZOO_CONNECTED_STATE)
180
181
  end
181
182
 
@@ -191,15 +192,12 @@ class CZookeeper
191
192
  end
192
193
 
193
194
  cnt = Continuation.new(meth, *args)
194
- @reg.lock
195
- begin
195
+ @reg.synchronize do |r|
196
196
  if meth == :state
197
- @reg.state_check << cnt
197
+ r.state_check << cnt
198
198
  else
199
- @reg.pending << cnt
199
+ r.pending << cnt
200
200
  end
201
- ensure
202
- @reg.unlock rescue nil
203
201
  end
204
202
  wake_event_loop!
205
203
  cnt.value
data/ext/extconf.rb CHANGED
@@ -42,6 +42,11 @@ $LDFLAGS = "#{$libraries} #{$LDFLAGS}"
42
42
  $LIBPATH = ["#{HERE}/lib"]
43
43
  $DEFLIBPATH = []
44
44
 
45
+ # fix for rbenv?
46
+ if $LIBRUBYARG == ''
47
+ $LIBRUBYARG = RbConfig::CONFIG['LIBRUBYARG']
48
+ end
49
+
45
50
  def safe_sh(cmd)
46
51
  puts cmd
47
52
  system(cmd)
@@ -87,6 +92,8 @@ Dir.chdir("#{HERE}/lib") do
87
92
  end
88
93
  $LIBS << " -lzookeeper_st_gem"
89
94
 
95
+ have_func('rb_thread_blocking_region')
96
+
90
97
  $CFLAGS << ' -Wall' if ZK_DEV
91
98
  create_makefile 'zookeeper_c'
92
99
 
data/ext/zkrb.c CHANGED
@@ -824,7 +824,6 @@ static VALUE method_zkrb_iterate_event_loop(VALUE self) {
824
824
  return INT2FIX(rc);
825
825
  }
826
826
 
827
-
828
827
  static VALUE method_has_events(VALUE self) {
829
828
  VALUE rb_event;
830
829
  FETCH_DATA_PTR(self, zk);
@@ -882,16 +881,8 @@ static VALUE method_recv_timeout(VALUE self) {
882
881
  // returns a CZookeeper::ClientId object with the values set for session_id and passwd
883
882
  static VALUE method_client_id(VALUE self) {
884
883
  FETCH_DATA_PTR(self, zk);
885
- char buf[32];
886
884
  const clientid_t *cid = zoo_client_id(zk->zh);
887
885
 
888
- /* if (strlen(cid->passwd) != 16) { */
889
- /* zkrb_debug("passwd is not null-terminated");*/
890
- /* } else {*/
891
- /* hexbufify(buf, cid->passwd, 16);*/
892
- /* zkrb_debug("password in hex is: %s", buf);*/
893
- /* }*/
894
-
895
886
  VALUE session_id = LL2NUM(cid->client_id);
896
887
  VALUE passwd = rb_str_new(cid->passwd, 16);
897
888
 
@@ -4,10 +4,10 @@
4
4
 
5
5
  VALUE zkrb_thread_blocking_region(zkrb_blocking_function_t *func, void *data1) {
6
6
 
7
- #ifdef ZKRB_RUBY_187
8
- return func(data1);
9
- #else
7
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
10
8
  return rb_thread_blocking_region((rb_blocking_function_t *)func, data1, RUBY_UBF_IO, 0);
9
+ #else
10
+ return func(data1);
11
11
  #endif
12
12
 
13
13
  }
@@ -31,17 +31,15 @@ class ZookeeperBase
31
31
  ZOO_LOG_LEVEL_DEBUG = 4
32
32
 
33
33
 
34
- def_delegators :@czk, :get_children, :exists, :delete, :get, :set,
34
+ def_delegators :czk, :get_children, :exists, :delete, :get, :set,
35
35
  :set_acl, :get_acl, :client_id, :sync, :wait_until_connected
36
36
 
37
- # some state methods need to be more paranoid about locking to ensure the correct
38
- # state is returned
39
- #
40
37
  def self.threadsafe_inquisitor(*syms)
41
38
  syms.each do |sym|
42
39
  class_eval(<<-EOM, __FILE__, __LINE__+1)
43
40
  def #{sym}
44
- false|@mutex.synchronize { @czk and @czk.#{sym} }
41
+ c = @mutex.synchronize { @czk }
42
+ false|(c && c.#{sym})
45
43
  end
46
44
  EOM
47
45
  end
@@ -117,7 +115,7 @@ class ZookeeperBase
117
115
  # if either of these happen, the user will need to renegotiate a connection via reopen
118
116
  def assert_open
119
117
  @mutex.synchronize do
120
- raise Exceptions::NotConnected if closed?
118
+ raise Exceptions::NotConnected if !@czk or @czk.closed?
121
119
  if forked?
122
120
  raise InheritedConnectionError, <<-EOS.gsub(/(?:^|\n)\s*/, ' ').strip
123
121
  You tried to use a connection inherited from another process
@@ -152,7 +150,7 @@ class ZookeeperBase
152
150
  # is pretty damn annoying. this is used to clean things up.
153
151
  def create(*args)
154
152
  # since we don't care about the inputs, just glob args
155
- rc, new_path = @mutex.synchronize { @czk.create(*args) }
153
+ rc, new_path = czk.create(*args)
156
154
  [rc, strip_chroot_from(new_path)]
157
155
  end
158
156
 
@@ -173,7 +171,7 @@ class ZookeeperBase
173
171
 
174
172
  def state
175
173
  return ZOO_CLOSED_STATE if closed?
176
- @mutex.synchronize { @czk.state }
174
+ czk.state
177
175
  end
178
176
 
179
177
  def session_id
@@ -190,7 +188,9 @@ class ZookeeperBase
190
188
 
191
189
  # we are closed if there is no @czk instance or @czk.closed?
192
190
  def closed?
193
- @mutex.synchronize { !@czk or @czk.closed? }
191
+ czk.closed?
192
+ rescue Exceptions::NotConnected
193
+ true
194
194
  end
195
195
 
196
196
  def pause_before_fork_in_parent
@@ -243,6 +243,12 @@ protected
243
243
  super(req_id, meth_name, call_opts)
244
244
  end
245
245
 
246
+ def czk
247
+ rval = @mutex.synchronize { @czk }
248
+ raise Exceptions::NotConnected unless rval
249
+ rval
250
+ end
251
+
246
252
  # if we're chrooted, this method will strip the chroot prefix from +path+
247
253
  def strip_chroot_from(path)
248
254
  return path unless (chrooted? and path and path.start_with?(chroot_path))
@@ -6,6 +6,8 @@ module Zookeeper
6
6
  include Constants
7
7
  include Logger
8
8
 
9
+ OPERATION_TIMEOUT = 30 # seconds
10
+
9
11
  # for keeping track of which continuations are pending, and which ones have
10
12
  # been submitted and are awaiting a repsonse
11
13
  #
@@ -79,8 +81,8 @@ module Zookeeper
79
81
  def initialize(meth, *args)
80
82
  @meth = meth
81
83
  @args = args
82
- @mutex = Mutex.new
83
- @cond = ConditionVariable.new
84
+ @mutex = Monitor.new
85
+ @cond = @mutex.new_cond
84
86
  @rval = nil
85
87
 
86
88
  # make this error reporting more robust if necessary, right now, just set to state
@@ -88,9 +90,28 @@ module Zookeeper
88
90
  end
89
91
 
90
92
  # the caller calls this method and receives the response from the async loop
93
+ # this method has a hard-coded 30 second timeout as a safety feature. No
94
+ # call should take more than 20s (as the session timeout is set to 20s)
95
+ # so if any call takes longer than that, something has gone horribly wrong.
96
+ #
97
+ # @raise [ContinuationTimeoutError] if a response is not received within 30s
98
+ #
91
99
  def value
100
+ time_to_stop = Time.now + OPERATION_TIMEOUT
101
+ now = nil
102
+
92
103
  @mutex.synchronize do
93
- @cond.wait(@mutex) until @rval or @error
104
+ while true
105
+ now = Time.now
106
+ break if @rval or @error or (now > time_to_stop)
107
+
108
+ deadline = time_to_stop.to_f - now.to_f
109
+ @cond.wait(deadline)
110
+ end
111
+
112
+ if now > time_to_stop
113
+ raise ContinuationTimeoutError, "response for meth: #{meth.inspect}, args: #{args.inspect}, not received within #{OPERATION_TIMEOUT} seconds"
114
+ end
94
115
 
95
116
  case @error
96
117
  when nil
@@ -84,6 +84,10 @@ stacktrace:
84
84
  # maybe use this for continuation
85
85
  class InterruptedException < ZookeeperException ; end
86
86
 
87
+ # raised when a continuation operation takes more time than is reasonable and
88
+ # the thread should be awoken. (i.e. prevents a call that never returns)
89
+ class ContinuationTimeoutError < ZookeeperException; end
90
+
87
91
  # raised when the user tries to use a connection after a fork()
88
92
  # without calling reopen() in the C client
89
93
  #
@@ -1,4 +1,4 @@
1
1
  module Zookeeper
2
- VERSION = '1.2.4'
2
+ VERSION = '1.2.5'
3
3
  DRIVER_VERSION = '3.3.5'
4
4
  end
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: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 2
9
- - 4
10
- version: 1.2.4
9
+ - 5
10
+ version: 1.2.5
11
11
  platform: java
12
12
  authors:
13
13
  - Phillip Pearson
@@ -20,7 +20,7 @@ autorequire:
20
20
  bindir: bin
21
21
  cert_chain: []
22
22
 
23
- date: 2012-05-24 00:00:00 Z
23
+ date: 2012-06-03 00:00:00 Z
24
24
  dependencies:
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: backports