zookeeper 1.2.6 → 1.2.7

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,22 @@
1
+ v1.2.7 further lock adjustments, deadlock risk reduction
2
+
3
+ * Refactor ZookeeperBase to not hold onto the mutex while waiting
4
+ for the dispatch thread to exit and the CZookeeper instance to close.
5
+ Instead, lock, nil out @czk, and unlock (which will cause all calls to
6
+ raise NotConnected), and then carry on with the shutdown procedure, greatly
7
+ reducing the chances of a deadlock. Also add a hardcoded 30 second timeout
8
+ to the join of the shutdown thread, that way we won't hang indefinitely in
9
+ the case of an unforseen condition.
10
+
11
+ * Improve the CZookeeper#wait_until_connected to use a deadline approach
12
+ to waiting for both running and connected states. Also, handle the
13
+ 'nil' (wait forever) timeout properly.
14
+
15
+ * Wake all waiting threads on all ConditionVariables when CZookeeper#shut_down!
16
+ is called
17
+
18
+ v1.2.6 fix build on fedora
19
+
1
20
  v1.2.5 cleanup locking in ZookeeperBase
2
21
 
3
22
  * There were several situations where we would hold the lock before calling
data/ext/c_zookeeper.rb CHANGED
@@ -161,7 +161,7 @@ class CZookeeper
161
161
 
162
162
  def state
163
163
  return ZOO_CLOSED_STATE if closed?
164
- submit_and_block(:state)
164
+ @mutex.synchronize { @state }
165
165
  end
166
166
 
167
167
  # this implementation is gross, but i don't really see another way of doing it
@@ -172,12 +172,22 @@ class CZookeeper
172
172
  # if timeout is nil, we never time out, and wait forever for CONNECTED state
173
173
  #
174
174
  def wait_until_connected(timeout=10)
175
+ time_to_stop = timeout ? Time.now + timeout : nil
175
176
 
176
177
  return false unless wait_until_running(timeout)
177
178
 
178
179
  @mutex.synchronize do
179
- # TODO: use deadline here
180
- @state_cond.wait(timeout) unless (@state == ZOO_CONNECTED_STATE)
180
+ while true
181
+ if timeout
182
+ now = Time.now
183
+ break if (@state == ZOO_CONNECTED_STATE) || @_shutting_down || @_closed || (now > time_to_stop)
184
+ delay = time_to_stop.to_f - now.to_f
185
+ @state_cond.wait(delay)
186
+ else
187
+ break if (@state == ZOO_CONNECTED_STATE) || @_shutting_down || @_closed
188
+ @state_cond.wait
189
+ end
190
+ end
181
191
  end
182
192
 
183
193
  connected?
@@ -343,7 +353,12 @@ class CZookeeper
343
353
  def shut_down!
344
354
  logger.debug { "##{__method__}" }
345
355
 
346
- @mutex.synchronize { @_shutting_down = true }
356
+ @mutex.synchronize do
357
+ @_shutting_down = true
358
+ # ollie ollie oxen all home free!
359
+ @state_cond.broadcast
360
+ @running_cond.broadcast
361
+ end
347
362
  end
348
363
 
349
364
  # called by underlying C code to signal we're running
@@ -355,11 +370,5 @@ class CZookeeper
355
370
  @running_cond.broadcast
356
371
  end
357
372
  end
358
-
359
- # def notify_state_change!
360
- # @mutex.synchronize do
361
- # @state_cond.broadcast
362
- # end
363
- # end
364
373
  end
365
374
  end
@@ -130,20 +130,34 @@ class ZookeeperBase
130
130
  # potentially dangerous and should only be called after a fork() to close
131
131
  # this instance
132
132
  def close!
133
- @czk && @czk.close
133
+ inst, @czk = @czk, nil
134
+ inst && inst.close
134
135
  end
135
136
 
136
137
  # close the connection normally, stops the dispatch thread and closes the
137
138
  # underlying connection cleanly
138
139
  def close
139
- shutdown_thread = Thread.new do
140
- @mutex.synchronize do
140
+ sd_thread = nil
141
+
142
+ @mutex.synchronize do
143
+ return unless @czk
144
+ inst, @czk = @czk, nil
145
+
146
+ sd_thread = Thread.new(inst) do |_inst|
141
147
  stop_dispatch_thread!
142
- close!
148
+ _inst.close
149
+ end
150
+ end
151
+
152
+ # if we're on the event dispatch thread for some stupid reason, then don't join
153
+ unless event_dispatch_thread?
154
+ # hard-coded 30 second delay, don't hang forever
155
+ if sd_thread.join(30) != sd_thread
156
+ logger.error { "timed out waiting for shutdown thread to exit" }
143
157
  end
144
158
  end
145
159
 
146
- shutdown_thread.join unless event_dispatch_thread?
160
+ nil
147
161
  end
148
162
 
149
163
  # the C lib doesn't strip the chroot path off of returned path values, which
@@ -10,15 +10,7 @@ module Common
10
10
  @dispatcher && (@dispatcher == Thread.current)
11
11
  end
12
12
 
13
- protected
14
- def get_next_event(blocking=true)
15
- @event_queue.pop(!blocking).tap do |event|
16
- logger.debug { "#{self.class}##{__method__} delivering event #{event.inspect}" }
17
- end
18
- rescue ThreadError
19
- nil
20
- end
21
-
13
+ private
22
14
  def setup_call(meth_name, opts)
23
15
  req_id = nil
24
16
  @mutex.synchronize {
@@ -71,21 +63,9 @@ protected
71
63
  return
72
64
  end
73
65
 
74
- logger.debug { "starting dispatch thread" }
75
-
76
- @dispatcher = Thread.new do
77
- while true
78
- begin
79
- dispatch_next_callback(get_next_event(true))
80
- rescue QueueWithPipe::ShutdownException
81
- logger.info { "dispatch thread exiting, got shutdown exception" }
82
- break
83
- rescue Exception => e
84
- $stderr.puts ["#{e.class}: #{e.message}", e.backtrace.map { |n| "\t#{n}" }.join("\n")].join("\n")
85
- end
86
- end
87
- signal_dispatch_thread_exit!
88
- end
66
+ logger.debug { "starting dispatch thread" }
67
+
68
+ @dispatcher = Thread.new(&method(:dispatch_thread_body))
89
69
  end
90
70
  end
91
71
 
@@ -120,11 +100,12 @@ protected
120
100
  end
121
101
  end
122
102
 
123
- def signal_dispatch_thread_exit!
124
- @mutex.synchronize do
125
- logger.debug { "dispatch thread exiting!" }
126
- @dispatch_shutdown_cond.broadcast
103
+ def get_next_event(blocking=true)
104
+ @event_queue.pop(!blocking).tap do |event|
105
+ logger.debug { "#{self.class}##{__method__} delivering event #{event.inspect}" }
127
106
  end
107
+ rescue ThreadError
108
+ nil
128
109
  end
129
110
 
130
111
  def dispatch_next_callback(hash)
@@ -177,7 +158,28 @@ protected
177
158
  end
178
159
  end
179
160
 
180
- private
161
+ def dispatch_thread_body
162
+ while true
163
+ begin
164
+ dispatch_next_callback(get_next_event(true))
165
+ rescue QueueWithPipe::ShutdownException
166
+ logger.info { "dispatch thread exiting, got shutdown exception" }
167
+ return
168
+ rescue Exception => e
169
+ $stderr.puts ["#{e.class}: #{e.message}", e.backtrace.map { |n| "\t#{n}" }.join("\n")].join("\n")
170
+ end
171
+ end
172
+ ensure
173
+ signal_dispatch_thread_exit!
174
+ end
175
+
176
+ def signal_dispatch_thread_exit!
177
+ @mutex.synchronize do
178
+ logger.debug { "dispatch thread exiting!" }
179
+ @dispatch_shutdown_cond.broadcast
180
+ end
181
+ end
182
+
181
183
  def prettify_event(hash)
182
184
  hash.dup.tap do |h|
183
185
  # pretty up the event display
@@ -1,4 +1,4 @@
1
1
  module Zookeeper
2
- VERSION = '1.2.6'
2
+ VERSION = '1.2.7'
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: 19
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 2
9
- - 6
10
- version: 1.2.6
9
+ - 7
10
+ version: 1.2.7
11
11
  platform: ruby
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-06-04 00:00:00 Z
23
+ date: 2012-06-05 00:00:00 Z
24
24
  dependencies:
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: backports