zookeeper-ng 1.5.2.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.ctags_paths +1 -0
  3. data/.dotfiles/ruby-gemset +1 -0
  4. data/.dotfiles/ruby-version +1 -0
  5. data/.dotfiles/rvmrc +2 -0
  6. data/.github/workflows/build.yml +57 -0
  7. data/.gitignore +19 -0
  8. data/.gitmodules +3 -0
  9. data/CHANGELOG +408 -0
  10. data/Gemfile +30 -0
  11. data/Guardfile +8 -0
  12. data/LICENSE +23 -0
  13. data/Manifest +29 -0
  14. data/README.markdown +62 -0
  15. data/Rakefile +121 -0
  16. data/cause-abort.rb +117 -0
  17. data/ext/.gitignore +6 -0
  18. data/ext/Rakefile +41 -0
  19. data/ext/c_zookeeper.rb +398 -0
  20. data/ext/common.h +17 -0
  21. data/ext/dbg.h +53 -0
  22. data/ext/depend +5 -0
  23. data/ext/event_lib.c +740 -0
  24. data/ext/event_lib.h +175 -0
  25. data/ext/extconf.rb +103 -0
  26. data/ext/generate_gvl_code.rb +321 -0
  27. data/ext/patches/zkc-3.3.5-network.patch +24 -0
  28. data/ext/patches/zkc-3.4.5-buffer-overflow.patch +11 -0
  29. data/ext/patches/zkc-3.4.5-config.patch +5454 -0
  30. data/ext/patches/zkc-3.4.5-fetch-and-add.patch +16 -0
  31. data/ext/patches/zkc-3.4.5-logging.patch +41 -0
  32. data/ext/patches/zkc-3.4.5-out-of-order-ping.patch +163 -0
  33. data/ext/patches/zkc-3.4.5-yosemite-htonl-fix.patch +102 -0
  34. data/ext/zkc-3.4.5.tar.gz +0 -0
  35. data/ext/zkrb.c +1080 -0
  36. data/ext/zkrb_wrapper.c +775 -0
  37. data/ext/zkrb_wrapper.h +350 -0
  38. data/ext/zkrb_wrapper_compat.c +15 -0
  39. data/ext/zkrb_wrapper_compat.h +11 -0
  40. data/ext/zookeeper_base.rb +256 -0
  41. data/java/java_base.rb +501 -0
  42. data/lib/zookeeper/acls.rb +44 -0
  43. data/lib/zookeeper/callbacks.rb +108 -0
  44. data/lib/zookeeper/client.rb +30 -0
  45. data/lib/zookeeper/client_methods.rb +282 -0
  46. data/lib/zookeeper/common/queue_with_pipe.rb +110 -0
  47. data/lib/zookeeper/common.rb +122 -0
  48. data/lib/zookeeper/compatibility.rb +138 -0
  49. data/lib/zookeeper/constants.rb +97 -0
  50. data/lib/zookeeper/continuation.rb +223 -0
  51. data/lib/zookeeper/core_ext.rb +58 -0
  52. data/lib/zookeeper/em_client.rb +55 -0
  53. data/lib/zookeeper/exceptions.rb +135 -0
  54. data/lib/zookeeper/forked.rb +19 -0
  55. data/lib/zookeeper/latch.rb +34 -0
  56. data/lib/zookeeper/logger/forwarding_logger.rb +84 -0
  57. data/lib/zookeeper/logger.rb +39 -0
  58. data/lib/zookeeper/monitor.rb +19 -0
  59. data/lib/zookeeper/rake_tasks.rb +165 -0
  60. data/lib/zookeeper/request_registry.rb +153 -0
  61. data/lib/zookeeper/stat.rb +21 -0
  62. data/lib/zookeeper/version.rb +4 -0
  63. data/lib/zookeeper.rb +115 -0
  64. data/notes.txt +14 -0
  65. data/scripts/upgrade-1.0-sed-alike.rb +46 -0
  66. data/spec/c_zookeeper_spec.rb +51 -0
  67. data/spec/chrooted_connection_spec.rb +83 -0
  68. data/spec/compatibilty_spec.rb +8 -0
  69. data/spec/default_watcher_spec.rb +41 -0
  70. data/spec/em_spec.rb +51 -0
  71. data/spec/ext/zookeeper_base_spec.rb +19 -0
  72. data/spec/forked_connection_spec.rb +122 -0
  73. data/spec/latch_spec.rb +24 -0
  74. data/spec/log4j.properties +17 -0
  75. data/spec/shared/all_success_return_values.rb +10 -0
  76. data/spec/shared/connection_examples.rb +1081 -0
  77. data/spec/spec_helper.rb +61 -0
  78. data/spec/support/00_logging.rb +38 -0
  79. data/spec/support/10_spawn_zookeeper.rb +20 -0
  80. data/spec/support/progress_formatter.rb +15 -0
  81. data/spec/support/zookeeper_spec_helpers.rb +96 -0
  82. data/spec/zookeeper_spec.rb +24 -0
  83. data/zookeeper.gemspec +46 -0
  84. data/zoomonkey/duplicates +3 -0
  85. data/zoomonkey/zoomonkey.rb +194 -0
  86. metadata +185 -0
data/Rakefile ADDED
@@ -0,0 +1,121 @@
1
+ release_ops_path = File.expand_path('../releaseops/lib', __FILE__)
2
+
3
+ # if the special submodule is availabe, use it
4
+ # we use a submodule because it doesn't depend on anything else (*cough* bundler)
5
+ # and can be shared across projects
6
+ #
7
+ if File.exists?(release_ops_path)
8
+ require File.join(release_ops_path, 'releaseops')
9
+
10
+ # sets up the multi-ruby zk:test_all rake tasks
11
+ ReleaseOps::TestTasks.define_for(*%w[1.8.7 1.9.2 jruby rbx ree 1.9.3])
12
+
13
+ # sets up the task :default => 'spec:run' and defines a simple
14
+ # "run the specs with the current rvm profile" task
15
+ ReleaseOps::TestTasks.define_simple_default_for_travis
16
+
17
+ # Define a task to run code coverage tests
18
+ ReleaseOps::TestTasks.define_simplecov_tasks
19
+
20
+ # set up yard:server, yard:gems, and yard:clean tasks
21
+ # for doing documentation stuff
22
+ ReleaseOps::YardTasks.define
23
+
24
+ task :clean => 'yard:clean'
25
+
26
+ namespace :zk do
27
+ namespace :gems do
28
+ desc "Build gems to prepare for a release. Requires TAG="
29
+ task :build do
30
+ require 'tmpdir'
31
+
32
+ raise "You must specify a TAG" unless ENV['TAG']
33
+
34
+ ReleaseOps.with_tmpdir(:prefix => 'zookeeper') do |tmpdir|
35
+ tag = ENV['TAG']
36
+
37
+ sh "git clone . #{tmpdir}"
38
+
39
+ orig_dir = Dir.getwd
40
+
41
+ Dir.chdir tmpdir do
42
+ sh "git co #{tag} && git reset --hard && git clean -fdx"
43
+
44
+ ENV['JAVA_GEM'] = nil
45
+ sh "gem build zookeeper.gemspec"
46
+ sh "env JAVA_GEM=1 gem build zookeeper.gemspec"
47
+
48
+ mv FileList['*.gem'], orig_dir
49
+ end
50
+ end
51
+ end
52
+
53
+ desc "Release gems that have been built"
54
+ task :push do
55
+ gems = FileList['*.gem']
56
+ raise "No gemfiles to push!" if gems.empty?
57
+
58
+ gems.each do |gem|
59
+ sh "gem push #{gem}"
60
+ end
61
+ end
62
+
63
+ task :clean do
64
+ rm_rf FileList['*.gem']
65
+ end
66
+
67
+ task :all => [:build, :push, :clean]
68
+ end
69
+ end
70
+ end
71
+
72
+ task :clobber do
73
+ rm_rf 'tmp'
74
+ end
75
+
76
+ # cargo culted from http://blog.flavorjon.es/2009/06/easily-valgrind-gdb-your-ruby-c.html
77
+ VALGRIND_BASIC_OPTS = '--num-callers=50 --error-limit=no --partial-loads-ok=yes --undef-value-errors=no --trace-children=yes'
78
+
79
+ task 'valgrind' do
80
+ Dir.chdir 'ext' do
81
+ sh "rake clean build"
82
+ end
83
+
84
+ sh "valgrind #{VALGRIND_BASIC_OPTS} bundle exec rspec spec"
85
+ end
86
+
87
+ namespace :build do
88
+ task :clean do
89
+ Dir.chdir 'ext' do
90
+ sh 'rake clean'
91
+ end
92
+
93
+ Rake::Task['build'].invoke
94
+ end
95
+
96
+ task :clobber do
97
+ Dir.chdir 'ext' do
98
+ sh 'rake clobber'
99
+ end
100
+
101
+ Rake::Task['build'].invoke
102
+ end
103
+ end
104
+
105
+ desc "Build C component"
106
+ task :build do
107
+ Dir.chdir 'ext' do
108
+ sh "rake"
109
+ end
110
+ end
111
+
112
+ task 'spec:run' => 'build:clean' unless defined?(::JRUBY_VERSION)
113
+
114
+ task 'ctags' do
115
+ sh 'bundle-ctags'
116
+ end
117
+
118
+ # because i'm a creature of habit
119
+ task 'mb:test_all' => 'zk:test_all'
120
+
121
+
data/cause-abort.rb ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'zookeeper'
4
+ require File.expand_path('../spec/support/zookeeper_spec_helpers', __FILE__)
5
+
6
+ class CauseAbort
7
+ include Zookeeper::Logger
8
+ include Zookeeper::SpecHelpers
9
+
10
+ attr_reader :path, :pids_root, :data
11
+
12
+ def initialize
13
+ @path = "/_zktest_"
14
+ @pids_root = "#{@path}/pids"
15
+ @data = 'underpants'
16
+ end
17
+
18
+ def before
19
+ @zk = Zookeeper.new('localhost:2181')
20
+ rm_rf(@zk, path)
21
+ logger.debug { "----------------< BEFORE: END >-------------------" }
22
+ end
23
+
24
+ def process_alive?(pid)
25
+ Process.kill(0, @pid)
26
+ true
27
+ rescue Errno::ESRCH
28
+ false
29
+ end
30
+
31
+ def wait_for_child_safely(pid, timeout=5)
32
+ time_to_stop = Time.now + timeout
33
+
34
+ until Time.now > time_to_stop
35
+ if a = Process.wait2(@pid, Process::WNOHANG)
36
+ return a.last
37
+ else
38
+ sleep(0.01)
39
+ end
40
+ end
41
+
42
+ nil
43
+ end
44
+
45
+ def try_pause_and_resume
46
+ @zk.pause
47
+ logger.debug { "paused" }
48
+ @zk.resume
49
+ logger.debug { "resumed" }
50
+ @zk.close
51
+ logger.debug { "closed" }
52
+ end
53
+
54
+ def run_test
55
+ logger.debug { "----------------< TEST: BEGIN >-------------------" }
56
+ @zk.wait_until_connected
57
+
58
+ mkdir_p(@zk, pids_root)
59
+
60
+ # the parent's pid path
61
+ @zk.create(:path => "#{pids_root}/#{$$}", :data => $$.to_s)
62
+
63
+ @latch = Zookeeper::Latch.new
64
+ @event = nil
65
+
66
+ cb = proc do |h|
67
+ logger.debug { "watcher called back: #{h.inspect}" }
68
+ @event = h
69
+ @latch.release
70
+ end
71
+
72
+ @zk.stat(:path => "#{pids_root}/child", :watcher => cb)
73
+
74
+ logger.debug { "-------------------> FORK <---------------------------" }
75
+
76
+ @pid = fork do
77
+ rand_sleep = rand()
78
+
79
+ $stderr.puts "sleeping for rand_sleep: #{rand_sleep}"
80
+ sleep(rand_sleep)
81
+
82
+ logger.debug { "reopening connection in child: #{$$}" }
83
+ @zk.reopen
84
+ logger.debug { "creating path" }
85
+ rv = @zk.create(:path => "#{pids_root}/child", :data => $$.to_s)
86
+ logger.debug { "created path #{rv[:path]}" }
87
+ @zk.close
88
+
89
+ logger.debug { "close finished" }
90
+ exit!(0)
91
+ end
92
+
93
+ event_waiter_th = Thread.new do
94
+ @latch.await(5) unless @event
95
+ @event
96
+ end
97
+
98
+ logger.debug { "waiting on child #{@pid}" }
99
+
100
+ status = wait_for_child_safely(@pid)
101
+ raise "Child process did not exit, likely hung" unless status
102
+
103
+ if event_waiter_th.join(5) == event_waiter_th
104
+ logger.warn { "event waiter has not received events" }
105
+ end
106
+
107
+ exit(@event.nil? ? 1 : 0)
108
+ end
109
+
110
+ def run
111
+ before
112
+ run_test
113
+ # try_pause_and_resume
114
+ end
115
+ end
116
+
117
+ CauseAbort.new.run
data/ext/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ bin
2
+ include
3
+ lib
4
+ *.bundle
5
+
6
+ c
data/ext/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ require 'rbconfig'
2
+
3
+ ZKRB_WRAPPER = %w[zkrb_wrapper.c zkrb_wrapper.h]
4
+
5
+ namespace :zkrb do
6
+ task :clean do
7
+ if File.exists?('Makefile')
8
+ sh 'make clean'
9
+ rm 'Makefile' # yep, regenerate this
10
+ else
11
+ $stderr.puts "nothing to clean, no Makefile"
12
+ end
13
+ end
14
+
15
+ task :clobber => :clean do
16
+ rm_rf %w[Makefile c lib bin include ._c] + ZKRB_WRAPPER
17
+ end
18
+ end
19
+
20
+ task :clean => 'zkrb:clean'
21
+ task :clobber => 'zkrb:clobber'
22
+ task :wrappers => ZKRB_WRAPPER
23
+ task :default => :build
24
+
25
+ file 'zkrb_wrapper.c' => 'generate_gvl_code.rb' do
26
+ sh "ruby generate_gvl_code.rb code"
27
+ end
28
+
29
+ file 'zkrb_wrapper.h' => 'generate_gvl_code.rb' do
30
+ sh "ruby generate_gvl_code.rb headers"
31
+ end
32
+
33
+ file 'Makefile' do
34
+ sh "ruby extconf.rb"
35
+ end
36
+
37
+ task :build => [ 'Makefile', :wrappers ] do
38
+ sh 'make'
39
+ end
40
+
41
+
@@ -0,0 +1,398 @@
1
+ Zookeeper.require_lib(
2
+ 'zookeeper/logger',
3
+ 'zookeeper/common',
4
+ 'zookeeper/constants',
5
+ 'zookeeper/exceptions' # zookeeper_c depends on exceptions defined in here
6
+ )
7
+
8
+ Zookeeper.require_root 'ext/zookeeper_c'
9
+
10
+ # require File.expand_path('../zookeeper_c', __FILE__)
11
+
12
+ module Zookeeper
13
+ # NOTE: this class extending (opening) the class defined in zkrb.c
14
+ class CZookeeper
15
+ include Forked
16
+ include Constants
17
+ include Exceptions
18
+ include Logger
19
+
20
+ DEFAULT_RECEIVE_TIMEOUT_MSEC = 10000
21
+
22
+ class GotNilEventException < StandardError; end
23
+
24
+ attr_accessor :original_pid
25
+
26
+ # assume we're at debug level
27
+ def self.get_debug_level
28
+ @debug_level ||= ZOO_LOG_LEVEL_INFO
29
+ end
30
+
31
+ def self.set_debug_level(value)
32
+ @debug_level = value
33
+ set_zkrb_debug_level(value)
34
+ end
35
+
36
+ # wrap these calls in our sync->async special sauce
37
+ %w[get set exists create delete get_acl set_acl get_children add_auth].each do |sym|
38
+ class_eval(<<-EOS, __FILE__, __LINE__+1)
39
+ def #{sym}(*args)
40
+ submit_and_block(:#{sym}, *args)
41
+ end
42
+ EOS
43
+ end
44
+
45
+ def initialize(host, event_queue, opts={})
46
+ @host = host
47
+ @event_queue = event_queue
48
+
49
+ # keep track of the pid that created us
50
+ update_pid!
51
+
52
+ # used by the C layer. CZookeeper sets this to true when the init method
53
+ # has completed. once this is set to true, it stays true.
54
+ #
55
+ # you should grab the @mutex before messing with this flag
56
+ @_running = nil
57
+
58
+ # This is set to true after destroy_zkrb_instance has been called and all
59
+ # CZookeeper state has been cleaned up
60
+ @_closed = false # also used by the C layer
61
+
62
+ # set by the ruby side to indicate we are in shutdown mode used by method_get_next_event
63
+ @_shutting_down = false
64
+
65
+ # the actual C data is stashed in this ivar. never *ever* touch this
66
+ @_data = nil
67
+
68
+ @_receive_timeout_msec = opts[:receive_timeout_msec] || DEFAULT_RECEIVE_TIMEOUT_MSEC
69
+
70
+ @mutex = Monitor.new
71
+
72
+ # used to signal that we're running
73
+ @running_cond = @mutex.new_cond
74
+
75
+ # used to signal we've received the connected event
76
+ @state_mutex = Monitor.new
77
+ @state_cond = @state_mutex.new_cond
78
+
79
+ # the current state of the connection
80
+ @state = ZOO_CLOSED_STATE
81
+
82
+ @pipe_read, @pipe_write = IO.pipe
83
+
84
+ @event_thread = nil
85
+
86
+ # hash of in-flight Continuation instances
87
+ @reg = Continuation::Registry.new
88
+
89
+ log_level = ENV['ZKC_DEBUG'] ? ZOO_LOG_LEVEL_DEBUG : ZOO_LOG_LEVEL_ERROR
90
+
91
+ logger.info { "initiating connection to #{@host}" }
92
+
93
+ zkrb_init(@host, opts)#, :zkc_log_level => log_level)
94
+
95
+ start_event_thread
96
+
97
+ logger.debug { "init returned!" }
98
+ end
99
+
100
+ def closed?
101
+ @mutex.synchronize { !!@_closed }
102
+ end
103
+
104
+ def running?
105
+ @mutex.synchronize { !!@_running }
106
+ end
107
+
108
+ def shutting_down?
109
+ @mutex.synchronize { !!@_shutting_down }
110
+ end
111
+
112
+ def connected?
113
+ state == ZOO_CONNECTED_STATE
114
+ end
115
+
116
+ def connecting?
117
+ state == ZOO_CONNECTING_STATE
118
+ end
119
+
120
+ def associating?
121
+ state == ZOO_ASSOCIATING_STATE
122
+ end
123
+
124
+ def close
125
+ return if closed?
126
+
127
+ fn_close = proc do
128
+ if !@_closed and @_data
129
+ logger.debug { "CALLING CLOSE HANDLE!!" }
130
+ close_handle
131
+ end
132
+ end
133
+
134
+ if forked?
135
+ fn_close.call
136
+ else
137
+ stop_event_thread
138
+ @mutex.synchronize(&fn_close)
139
+ end
140
+
141
+ [@pipe_read, @pipe_write].each { |io| io.close unless io.closed? }
142
+
143
+ nil
144
+ end
145
+
146
+ # call this to stop the event loop, you can resume with the
147
+ # resume method
148
+ #
149
+ # requests may still be added during this time, but they will not be
150
+ # processed until you call resume
151
+ def pause_before_fork_in_parent
152
+ logger.debug { "##{__method__}" }
153
+ @mutex.synchronize { stop_event_thread }
154
+ end
155
+
156
+ # call this if 'pause' was previously called to start the event loop again
157
+ def resume_after_fork_in_parent
158
+ logger.debug { "##{__method__}" }
159
+
160
+ @mutex.synchronize do
161
+ @_shutting_down = nil
162
+ start_event_thread
163
+ end
164
+ end
165
+
166
+ def state
167
+ return ZOO_CLOSED_STATE if closed?
168
+ @state_mutex.synchronize { @state }
169
+ end
170
+
171
+ # this implementation is gross, but i don't really see another way of doing it
172
+ # without more grossness
173
+ #
174
+ # returns true if we're connected, false if we're not
175
+ #
176
+ # if timeout is nil, we never time out, and wait forever for CONNECTED state
177
+ #
178
+ def wait_until_connected(timeout=10)
179
+ time_to_stop = timeout ? Time.now + timeout : nil
180
+
181
+ return false unless wait_until_running(timeout)
182
+
183
+ @state_mutex.synchronize do
184
+ while true
185
+ if timeout
186
+ now = Time.now
187
+ break if (@state == ZOO_CONNECTED_STATE) || unhealthy? || (now > time_to_stop)
188
+ delay = time_to_stop.to_f - now.to_f
189
+ @state_cond.wait(delay)
190
+ else
191
+ break if (@state == ZOO_CONNECTED_STATE) || unhealthy?
192
+ @state_cond.wait
193
+ end
194
+ end
195
+ end
196
+
197
+ connected?
198
+ end
199
+
200
+ private
201
+ # This method is NOT SYNCHRONIZED!
202
+ #
203
+ # you must hold the @mutex lock while calling this method
204
+ def unhealthy?
205
+ @_closed || @_shutting_down || is_unrecoverable
206
+ end
207
+
208
+ # This method is NOT SYNCHRONIZED!
209
+ #
210
+ # you must hold the @mutex lock while calling this method
211
+ def healthy?
212
+ !unhealthy?
213
+ end
214
+
215
+ # submits a job for processing
216
+ # blocks the caller until result has returned
217
+ def submit_and_block(meth, *args)
218
+ @mutex.synchronize do
219
+ raise Exceptions::NotConnected if unhealthy?
220
+ end
221
+
222
+ cnt = Continuation.new(meth, *args)
223
+ @reg.synchronize do |r|
224
+ if meth == :state
225
+ r.state_check << cnt
226
+ else
227
+ r.pending << cnt
228
+ end
229
+ end
230
+ wake_event_loop!
231
+ cnt.value
232
+ end
233
+
234
+ # this method is part of the reopen/close code, and is responsible for
235
+ # shutting down the dispatch thread.
236
+ #
237
+ # this method must be EXTERNALLY SYNCHRONIZED!
238
+ #
239
+ # @event_thread will be nil when this method exits
240
+ #
241
+ def stop_event_thread
242
+ if @event_thread
243
+ logger.debug { "##{__method__}" }
244
+ shut_down!
245
+ wake_event_loop!
246
+ @event_thread.join
247
+ @event_thread = nil
248
+ end
249
+ end
250
+
251
+ # starts the event thread running if not already started
252
+ # returns false if already running
253
+ def start_event_thread
254
+ return false if @event_thread
255
+ @event_thread = Thread.new(&method(:event_thread_body))
256
+ end
257
+
258
+ # will wait until the client has entered the running? state
259
+ # or until timeout seconds have passed.
260
+ #
261
+ # returns true if we're running, false if we timed out
262
+ def wait_until_running(timeout=5)
263
+ @mutex.synchronize do
264
+ return true if @_running
265
+ @running_cond.wait(timeout)
266
+ !!@_running
267
+ end
268
+ end
269
+
270
+ def event_thread_body
271
+ Thread.current.abort_on_exception = true
272
+ logger.debug { "##{__method__} starting event thread" }
273
+
274
+ event_thread_await_running
275
+
276
+ # this is the main loop
277
+ while healthy?
278
+ if @reg.anything_to_do? && connected?
279
+ submit_pending_calls
280
+ end
281
+
282
+ zkrb_iterate_event_loop
283
+ iterate_event_delivery
284
+ end
285
+
286
+ # ok, if we're exiting the event loop, and we still have a valid connection
287
+ # and there's still completions we're waiting to hear about, then we
288
+ # should pump the handle before leaving this loop
289
+ if @_shutting_down and not (@_closed or is_unrecoverable)
290
+ logger.debug { "we're in shutting down state, there are #{@reg.in_flight.length} in_flight completions" }
291
+
292
+ until @reg.in_flight.empty? or @_closed or is_unrecoverable
293
+ zkrb_iterate_event_loop
294
+ iterate_event_delivery
295
+ logger.debug { "there are #{@reg.in_flight} in_flight completions left" }
296
+ end
297
+
298
+ logger.debug { "finished completions" }
299
+ end
300
+
301
+ # anything left over after all that gets the finger
302
+ remaining = @reg.next_batch + @reg.in_flight.values
303
+
304
+ logger.debug { "there are #{remaining.length} completions to awaken" }
305
+
306
+ @reg.in_flight.clear
307
+
308
+ while cb = remaining.shift
309
+ cb.shutdown!
310
+ end
311
+ rescue ShuttingDownException
312
+ logger.error { "event thread saw @_shutting_down, bailing without entering loop" }
313
+ ensure
314
+ logger.debug { "##{__method__} exiting" }
315
+ end
316
+
317
+ def submit_pending_calls
318
+ calls = @reg.next_batch()
319
+
320
+ return if calls.empty?
321
+
322
+ while cntn = calls.shift
323
+ cntn.submit(self) # this delivers state check results (and does other stuff)
324
+ if req_id = cntn.req_id # state checks will not have a req_id
325
+ @reg.in_flight[req_id] = cntn # in_flight is only ever touched by us
326
+ end
327
+ end
328
+ end
329
+
330
+ def wake_event_loop!
331
+ @pipe_write && !@pipe_write.closed? && @pipe_write.write('1')
332
+ end
333
+
334
+ def iterate_event_delivery
335
+ while hash = zkrb_get_next_event_st()
336
+ logger.debug { "##{__method__} got #{hash.inspect} " }
337
+
338
+ if (hash[:req_id] == ZKRB_GLOBAL_CB_REQ) && (hash[:type] == -1)
339
+ ev_state = hash[:state]
340
+
341
+ @state_mutex.synchronize do
342
+ if @state != ev_state
343
+ @state = ev_state
344
+ @state_cond.broadcast
345
+ end
346
+ end
347
+ end
348
+
349
+ cntn = @reg.in_flight.delete(hash[:req_id])
350
+
351
+ if cntn and not cntn.user_callback? # this is one of "our" continuations
352
+ cntn.call(hash) # so we handle delivering it
353
+ next # and skip handing it to the dispatcher
354
+ end
355
+
356
+ # otherwise, the event was a session event (ZKRB_GLOBAL_CB_REQ)
357
+ # or a user-provided callback
358
+ @event_queue.push(hash)
359
+ end
360
+ end
361
+
362
+ def event_thread_await_running
363
+ logger.debug { "event_thread waiting until running: #{@_running}" }
364
+
365
+ @mutex.synchronize do
366
+ @running_cond.wait_until { @_running or @_shutting_down }
367
+ logger.debug { "event_thread running: #{@_running}" }
368
+
369
+ raise ShuttingDownException if @_shutting_down
370
+ end
371
+ end
372
+
373
+ # use this method to set the @_shutting_down flag to true
374
+ def shut_down!
375
+ logger.debug { "##{__method__}" }
376
+
377
+ @mutex.synchronize do
378
+ @_shutting_down = true
379
+ # ollie ollie oxen all home free!
380
+ @running_cond.broadcast
381
+ end
382
+
383
+ @state_mutex.synchronize do
384
+ @state_cond.broadcast
385
+ end
386
+ end
387
+
388
+ # called by underlying C code to signal we're running
389
+ def zkc_set_running_and_notify!
390
+ logger.debug { "##{__method__}" }
391
+
392
+ @mutex.synchronize do
393
+ @_running = true
394
+ @running_cond.broadcast
395
+ end
396
+ end
397
+ end
398
+ end
data/ext/common.h ADDED
@@ -0,0 +1,17 @@
1
+ #ifndef ZKRB_COMMON_H
2
+ #define ZKRB_COMMON_H
3
+
4
+ #include "ruby.h"
5
+
6
+ //#define THREADED
7
+ #undef THREADED // we are linking against the zookeeper_st lib, this is crucial
8
+
9
+ #ifndef RB_GC_GUARD_PTR
10
+ #define RB_GC_GUARD_PTR(V) (V);
11
+ #endif
12
+ #ifndef RB_GC_GUARD
13
+ #define RB_GC_GUARD(V) (V);
14
+ #endif
15
+
16
+
17
+ #endif /* ZKRB_COMMON_H */