zookeeper 1.0.6 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.dotfiles/rvmrc +1 -0
- data/.travis.yml +5 -0
- data/CHANGELOG +13 -0
- data/Gemfile +3 -0
- data/Guardfile +6 -0
- data/README.markdown +16 -12
- data/Rakefile +5 -0
- data/cause-abort.rb +117 -0
- data/ext/Rakefile +34 -21
- data/ext/c_zookeeper.rb +181 -70
- data/ext/common.h +7 -0
- data/ext/depend +2 -2
- data/ext/{zookeeper_lib.c → event_lib.c} +107 -66
- data/ext/{zookeeper_lib.h → event_lib.h} +4 -3
- data/ext/extconf.rb +12 -8
- data/ext/generate_gvl_code.rb +9 -2
- data/ext/{zookeeper_c.c → zkrb.c} +415 -176
- data/ext/zookeeper_base.rb +7 -26
- data/lib/zookeeper/client_methods.rb +1 -1
- data/lib/zookeeper/common.rb +3 -2
- data/lib/zookeeper/constants.rb +1 -0
- data/lib/zookeeper/continuation.rb +155 -0
- data/lib/zookeeper/exceptions.rb +7 -0
- data/lib/zookeeper/logger.rb +7 -0
- data/lib/zookeeper/monitor.rb +19 -0
- data/lib/zookeeper/version.rb +1 -1
- data/lib/zookeeper.rb +3 -0
- data/spec/forked_connection_spec.rb +11 -4
- data/spec/shared/connection_examples.rb +24 -22
- data/spec/spec_helper.rb +2 -2
- data/spec/support/zookeeper_spec_helpers.rb +1 -1
- metadata +12 -9
- data/spec/fork_hook_specs.rb +0 -53
data/.dotfiles/rvmrc
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
v1.1.0 Rewrite C backend to use zookeeper_st, the async library
|
2
|
+
|
3
|
+
* In order to ensure fork safety, a rewrite of the backend was necessary.
|
4
|
+
It was impossible to guarantee with the mt lib that a lock would not
|
5
|
+
be held by a thread when fork() was called, which opened up the possibility
|
6
|
+
for corruption and other badness.
|
7
|
+
|
8
|
+
This version contains a Continuation class, which allows us to present a
|
9
|
+
synchronous front-end to the asynchronous backend. All features are still
|
10
|
+
supported, no special action is necessary to prepare for a fork, and the
|
11
|
+
post-fork procedure is the same as before: call reopen() in the child,
|
12
|
+
continue on in the parent like nothing happened.
|
13
|
+
|
1
14
|
v1.0.6 Only include backports if RUBY_VERSION is 1.8.x
|
2
15
|
|
3
16
|
* 'backports' pollutes too much, use sparingly
|
data/Gemfile
CHANGED
data/Guardfile
ADDED
data/README.markdown
CHANGED
@@ -6,6 +6,10 @@ An interface to the Zookeeper cluster coordination server.
|
|
6
6
|
|
7
7
|
For a higher-level interface with a more convenient API and features such as locks, have a look at [ZK](https://github.com/slyphon/zk) (also available is [ZK-EventMachine](https://github.com/slyphon/zk-eventmachine) for those who prefer async).
|
8
8
|
|
9
|
+
## Fork Safety! ##
|
10
|
+
|
11
|
+
As of 1.1.0, this library is fork-safe (which was not easy to accomplish). This means you can use it without worry in unicorn, resque, and whatever other fork-philic frameworks you sick little monkeys are using this week. The only rule is that after a fork(), you need to call `#reopen` on the client ASAP, because if you try to peform any other action, an exception will be raised. Other than that, there is no special action that is needed in the parent.
|
12
|
+
|
9
13
|
## Big Plans for 1.0 ##
|
10
14
|
|
11
15
|
The 1.0 release will feature a reorganization of the heirarchy. There will be a single top-level `Zookeeper` namespace (as opposed to the current layout, with 5-6 different top-level constants), and for the next several releases, there will be a backwards compatible require for users that still need to use the old names.
|
@@ -36,18 +40,18 @@ Connect to a server:
|
|
36
40
|
## Idioms
|
37
41
|
|
38
42
|
The following methods are initially supported:
|
39
|
-
* get
|
40
|
-
* set
|
41
|
-
*
|
42
|
-
* stat
|
43
|
-
* create
|
44
|
-
* delete
|
45
|
-
*
|
46
|
-
*
|
47
|
-
|
48
|
-
All support async callbacks.
|
49
|
-
|
50
|
-
Calls take a dictionary of parameters.
|
43
|
+
* `get`
|
44
|
+
* `set`
|
45
|
+
* `get_children`
|
46
|
+
* `stat`
|
47
|
+
* `create`
|
48
|
+
* `delete`
|
49
|
+
* `get_acl`
|
50
|
+
* `set_acl`
|
51
|
+
|
52
|
+
All support async callbacks. `get`, `get_children` and `stat` support both watchers and callbacks.
|
53
|
+
|
54
|
+
Calls take a dictionary of parameters. With the exception of set\_acl, the only required parameter is `:path`. Each call returns a dictionary with at minimum two keys :req\_id and :rc.
|
51
55
|
|
52
56
|
### A Bit about this repository ###
|
53
57
|
|
data/Rakefile
CHANGED
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/Rakefile
CHANGED
@@ -1,20 +1,41 @@
|
|
1
|
+
require 'rbconfig'
|
1
2
|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
LIB_ZK_SO = 'lib/libzookeeper_mt_gem.la'
|
4
|
+
|
5
|
+
TARBALL = FileList['zkc-*.tar.gz'].first
|
6
|
+
|
7
|
+
raise "Where is the zkc tarball!?" unless TARBALL
|
8
|
+
|
9
|
+
namespace :zkrb do
|
10
|
+
task :clean do
|
11
|
+
if File.exists?('Makefile')
|
12
|
+
sh 'make clean'
|
13
|
+
rm 'Makefile' # yep, regenerate this
|
14
|
+
else
|
15
|
+
$stderr.puts "nothing to clean, no Makefile"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
task :clobber => :clean do
|
20
|
+
rm_rf %w[Makefile c lib bin include]
|
7
21
|
end
|
8
22
|
end
|
9
23
|
|
24
|
+
task :clean => 'zkrb:clean'
|
25
|
+
task :clobber => 'zkrb:clobber'
|
26
|
+
|
10
27
|
GENERATE_GVL_CODE_RB = 'generate_gvl_code.rb'
|
11
28
|
|
12
|
-
file 'c' do
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
29
|
+
# file 'c' do
|
30
|
+
# if tarball = Dir['zkc-*.tar.gz'].first
|
31
|
+
# sh "tar -zxf #{tarball}"
|
32
|
+
# else
|
33
|
+
# raise "couldn't find the tarball! wtf?!"
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
|
37
|
+
file 'c' => TARBALL do
|
38
|
+
sh "tar -zxf #{TARBALL}"
|
18
39
|
end
|
19
40
|
|
20
41
|
file GENERATE_GVL_CODE_RB => 'c'
|
@@ -29,20 +50,12 @@ end
|
|
29
50
|
|
30
51
|
ZKRB_WRAPPER = %w[zkrb_wrapper.c zkrb_wrapper.h]
|
31
52
|
|
32
|
-
|
33
53
|
task :wrappers => ZKRB_WRAPPER
|
34
54
|
|
35
|
-
|
36
|
-
|
37
|
-
rm_rf %w[Makefile c lib bin include]
|
55
|
+
file 'Makefile' do
|
56
|
+
sh "ruby extconf.rb"
|
38
57
|
end
|
39
58
|
|
40
|
-
task :build_zkc do
|
41
|
-
sh 'ruby extconf.rb'
|
42
|
-
end
|
43
|
-
|
44
|
-
file 'Makefile' => :build_zkc
|
45
|
-
|
46
59
|
task :build => [ZKRB_WRAPPER, 'Makefile'].flatten do
|
47
60
|
sh 'make'
|
48
61
|
end
|
data/ext/c_zookeeper.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require_relative '../lib/zookeeper/logger'
|
2
2
|
require_relative '../lib/zookeeper/common'
|
3
3
|
require_relative '../lib/zookeeper/constants'
|
4
|
+
require_relative '../lib/zookeeper/exceptions' # zookeeper_c depends on exceptions defined in here
|
4
5
|
require_relative 'zookeeper_c'
|
5
6
|
|
6
7
|
# require File.expand_path('../zookeeper_c', __FILE__)
|
7
8
|
|
8
|
-
# TODO: see if we can get the destructor to handle thread/event queue teardown
|
9
|
-
# when we're garbage collected
|
10
9
|
module Zookeeper
|
10
|
+
# NOTE: this class extending (opening) the class defined in zkrb.c
|
11
11
|
class CZookeeper
|
12
12
|
include Forked
|
13
13
|
include Constants
|
@@ -30,6 +30,15 @@ class CZookeeper
|
|
30
30
|
set_zkrb_debug_level(value)
|
31
31
|
end
|
32
32
|
|
33
|
+
# wrap these calls in our sync->async special sauce
|
34
|
+
%w[get set exists create delete get_acl set_acl get_children].each do |sym|
|
35
|
+
class_eval(<<-EOS, __FILE__, __LINE__+1)
|
36
|
+
def #{sym}(*args)
|
37
|
+
submit_and_block(:#{sym}, *args)
|
38
|
+
end
|
39
|
+
EOS
|
40
|
+
end
|
41
|
+
|
33
42
|
def initialize(host, event_queue, opts={})
|
34
43
|
@host = host
|
35
44
|
@event_queue = event_queue
|
@@ -40,7 +49,7 @@ class CZookeeper
|
|
40
49
|
# used by the C layer. CZookeeper sets this to true when the init method
|
41
50
|
# has completed. once this is set to true, it stays true.
|
42
51
|
#
|
43
|
-
# you should grab the @
|
52
|
+
# you should grab the @mutex before messing with this flag
|
44
53
|
@_running = nil
|
45
54
|
|
46
55
|
# This is set to true after destroy_zkrb_instance has been called and all
|
@@ -55,40 +64,40 @@ class CZookeeper
|
|
55
64
|
|
56
65
|
@_session_timeout_msec = DEFAULT_SESSION_TIMEOUT_MSEC
|
57
66
|
|
58
|
-
|
59
|
-
raise ArgumentError, "opts[:client_id] must be a CZookeeper::ClientId" unless cid.kind_of?(ClientId)
|
60
|
-
raise ArgumentError, "session_id must not be nil" if cid.session_id.nil?
|
61
|
-
end
|
62
|
-
|
63
|
-
@_client_id = opts[:client_id]
|
64
|
-
|
65
|
-
@start_stop_mutex = Monitor.new
|
67
|
+
@mutex = Monitor.new
|
66
68
|
|
67
69
|
# used to signal that we're running
|
68
|
-
@running_cond = @
|
70
|
+
@running_cond = @mutex.new_cond
|
69
71
|
|
70
72
|
# used to signal we've received the connected event
|
71
|
-
@connected_cond = @
|
73
|
+
@connected_cond = @mutex.new_cond
|
74
|
+
|
75
|
+
@pipe_read, @pipe_write = IO.pipe
|
72
76
|
|
73
77
|
@event_thread = nil
|
74
78
|
|
75
|
-
|
79
|
+
# hash of in-flight Continuation instances
|
80
|
+
@reg = Continuation::Registry.new
|
81
|
+
|
82
|
+
log_level = ENV['ZKC_DEBUG'] ? ZOO_LOG_LEVEL_DEBUG : ZOO_LOG_LEVEL_ERROR
|
83
|
+
|
84
|
+
zkrb_init(@host)#, :zkc_log_level => log_level)
|
76
85
|
|
77
|
-
|
86
|
+
start_event_thread
|
78
87
|
|
79
88
|
logger.debug { "init returned!" }
|
80
89
|
end
|
81
90
|
|
82
91
|
def closed?
|
83
|
-
@
|
92
|
+
@mutex.synchronize { !!@_closed }
|
84
93
|
end
|
85
94
|
|
86
95
|
def running?
|
87
|
-
@
|
96
|
+
@mutex.synchronize { !!@_running }
|
88
97
|
end
|
89
98
|
|
90
99
|
def shutting_down?
|
91
|
-
@
|
100
|
+
@mutex.synchronize { !!@_shutting_down }
|
92
101
|
end
|
93
102
|
|
94
103
|
def connected?
|
@@ -116,13 +125,34 @@ class CZookeeper
|
|
116
125
|
if forked?
|
117
126
|
fn_close.call
|
118
127
|
else
|
119
|
-
|
120
|
-
|
121
|
-
@start_stop_mutex.synchronize(&fn_close)
|
128
|
+
stop_event_thread
|
129
|
+
@mutex.synchronize(&fn_close)
|
122
130
|
end
|
123
131
|
|
132
|
+
[@pipe_read, @pipe_write].each { |io| io.close unless io.closed? }
|
133
|
+
|
124
134
|
nil
|
125
135
|
end
|
136
|
+
|
137
|
+
# call this to stop the event loop, you can resume with the
|
138
|
+
# resume method
|
139
|
+
#
|
140
|
+
# requests may still be added during this time, but they will not be
|
141
|
+
# processed until you call resume
|
142
|
+
def pause
|
143
|
+
logger.debug { "#{self.class}##{__method__}" }
|
144
|
+
@mutex.synchronize { stop_event_thread }
|
145
|
+
end
|
146
|
+
|
147
|
+
# call this if 'pause' was previously called to start the event loop again
|
148
|
+
def resume
|
149
|
+
logger.debug { "#{self.class}##{__method__}" }
|
150
|
+
|
151
|
+
@mutex.synchronize do
|
152
|
+
@_shutting_down = nil
|
153
|
+
start_event_thread
|
154
|
+
end
|
155
|
+
end
|
126
156
|
|
127
157
|
def state
|
128
158
|
return ZOO_CLOSED_STATE if closed?
|
@@ -137,107 +167,188 @@ class CZookeeper
|
|
137
167
|
# if timeout is nil, we never time out, and wait forever for CONNECTED state
|
138
168
|
#
|
139
169
|
def wait_until_connected(timeout=10)
|
140
|
-
|
141
|
-
|
142
|
-
@connected_cond.wait(timeout) unless connected?
|
143
|
-
end
|
170
|
+
# this begin/ensure/end style is recommended by tarceri
|
171
|
+
# no need to create a context for every mutex grab
|
144
172
|
|
173
|
+
wait_until_running(timeout)
|
174
|
+
|
175
|
+
Thread.pass until connected? or is_unrecoverable
|
145
176
|
connected?
|
146
177
|
end
|
147
178
|
|
179
|
+
|
148
180
|
private
|
181
|
+
# submits a job for processing
|
182
|
+
# blocks the caller until result has returned
|
183
|
+
def submit_and_block(meth, *args)
|
184
|
+
cnt = Continuation.new(meth, *args)
|
185
|
+
@reg.synchronized { |r| r.pending << cnt }
|
186
|
+
wake_event_loop!
|
187
|
+
cnt.value
|
188
|
+
end
|
189
|
+
|
190
|
+
# this method is part of the reopen/close code, and is responsible for
|
191
|
+
# shutting down the dispatch thread.
|
192
|
+
#
|
193
|
+
# @event_thread will be nil when this method exits
|
194
|
+
#
|
195
|
+
def stop_event_thread
|
196
|
+
if @event_thread
|
197
|
+
logger.debug { "#{self.class}##{__method__}" }
|
198
|
+
shut_down!
|
199
|
+
wake_event_loop!
|
200
|
+
@event_thread.join
|
201
|
+
@event_thread = nil
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# starts the event thread running if not already started
|
206
|
+
# returns false if already running
|
207
|
+
def start_event_thread
|
208
|
+
return false if @event_thread
|
209
|
+
@event_thread = Thread.new(&method(:event_thread_body))
|
210
|
+
end
|
211
|
+
|
149
212
|
# will wait until the client has entered the running? state
|
150
213
|
# or until timeout seconds have passed.
|
151
214
|
#
|
152
215
|
# returns true if we're running, false if we timed out
|
153
|
-
def wait_until_running(timeout=5)
|
154
|
-
@
|
216
|
+
def wait_until_running(timeout=5)
|
217
|
+
@mutex.lock
|
218
|
+
begin
|
155
219
|
return true if @_running
|
156
220
|
@running_cond.wait(timeout)
|
157
221
|
!!@_running
|
222
|
+
ensure
|
223
|
+
@mutex.unlock
|
158
224
|
end
|
159
225
|
end
|
160
226
|
|
161
|
-
def
|
162
|
-
|
163
|
-
|
227
|
+
def event_thread_body
|
228
|
+
Thread.current.abort_on_exception = true
|
229
|
+
logger.debug { "#{self.class}##{__method__} starting event thread" }
|
164
230
|
|
165
|
-
|
166
|
-
|
231
|
+
event_thread_await_running
|
232
|
+
|
233
|
+
# this is the main loop
|
234
|
+
until (@_shutting_down or @_closed or is_unrecoverable)
|
235
|
+
submit_pending_calls if @reg.pending?
|
236
|
+
# log_realtime("zkrb_iterate_event_loop") do
|
237
|
+
zkrb_iterate_event_loop # XXX: check rc here
|
238
|
+
# end
|
239
|
+
iterate_event_delivery
|
240
|
+
end
|
167
241
|
|
168
|
-
|
169
|
-
|
242
|
+
# ok, if we're exiting the event loop, and we still have a valid connection
|
243
|
+
# and there's still completions we're waiting to hear about, then we
|
244
|
+
# should pump the handle before leaving this loop
|
245
|
+
if @_shutting_down and not (@_closed or is_unrecoverable or @reg.in_flight.empty?)
|
246
|
+
logger.debug { "we're in shutting down state, ensuring we have no in-flight completions" }
|
170
247
|
|
171
|
-
|
172
|
-
|
173
|
-
|
248
|
+
until @reg.in_flight.empty?
|
249
|
+
zkrb_iterate_event_loop
|
250
|
+
iterate_event_delivery
|
174
251
|
end
|
252
|
+
|
253
|
+
logger.debug { "finished completions" }
|
175
254
|
end
|
176
255
|
|
177
|
-
|
256
|
+
rescue ShuttingDownException
|
257
|
+
logger.error { "event thread saw @_shutting_down, bailing without entering loop" }
|
258
|
+
ensure
|
259
|
+
logger.debug { "#{self.class}##{__method__} exiting" }
|
260
|
+
end
|
178
261
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
262
|
+
def submit_pending_calls
|
263
|
+
# this is ok, because the calling thread only ever *adds* to this hash,
|
264
|
+
# and the keys are always unique
|
265
|
+
|
266
|
+
pending = nil
|
267
|
+
|
268
|
+
@reg.lock
|
269
|
+
begin
|
270
|
+
pending, @reg.pending = @reg.pending, []
|
271
|
+
ensure
|
272
|
+
@reg.unlock
|
186
273
|
end
|
274
|
+
|
275
|
+
return if pending.empty?
|
276
|
+
|
277
|
+
logger.debug { "#{self.class}##{__method__} " }
|
278
|
+
|
279
|
+
while cntn = pending.shift
|
280
|
+
cntn.submit(self)
|
281
|
+
@reg.in_flight[cntn.req_id] = cntn # in_flight is only ever touched by us
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def wake_event_loop!
|
286
|
+
logger.debug { "#{self.class}##{__method__}" }
|
287
|
+
@pipe_write && @pipe_write.write("\001")
|
187
288
|
end
|
188
289
|
|
189
|
-
def
|
190
|
-
|
191
|
-
|
290
|
+
def iterate_event_delivery
|
291
|
+
while hash = zkrb_get_next_event_st()
|
292
|
+
logger.debug { "#{self.class}##{__method__} got #{hash.inspect} " }
|
192
293
|
|
193
|
-
#
|
194
|
-
if hash.values_at(:req_id, :type, :state) == CONNECTED_EVENT_VALUES
|
294
|
+
# notify when we get this event so we know we're connected
|
295
|
+
if hash.values_at(:req_id, :type, :state) == CONNECTED_EVENT_VALUES[1..2]
|
195
296
|
notify_connected!
|
196
297
|
end
|
197
298
|
|
299
|
+
cntn = @reg.in_flight.delete(hash[:req_id])
|
300
|
+
|
301
|
+
if cntn and not cntn.user_callback? # this is one of "our" continuations
|
302
|
+
cntn.call(hash) # so we handle delivering it
|
303
|
+
next # and skip handing it to the dispatcher
|
304
|
+
end
|
305
|
+
|
306
|
+
# otherwise, the event was a session event (ZKRB_GLOBAL_CB_REQ)
|
307
|
+
# or a user-provided callback
|
198
308
|
@event_queue.push(hash)
|
199
309
|
end
|
200
310
|
end
|
201
311
|
|
202
|
-
|
203
|
-
|
204
|
-
logger.debug { "#{self.class}##{__method__}" }
|
312
|
+
def event_thread_await_running
|
313
|
+
logger.debug { "event_thread waiting until running: #{@_running}" }
|
205
314
|
|
206
|
-
@
|
207
|
-
|
315
|
+
@mutex.lock
|
316
|
+
begin
|
317
|
+
@running_cond.wait_until { @_running or @_shutting_down }
|
318
|
+
logger.debug { "event_thread running: #{@_running}" }
|
319
|
+
|
320
|
+
raise ShuttingDownException if @_shutting_down
|
321
|
+
ensure
|
322
|
+
@mutex.unlock
|
208
323
|
end
|
209
324
|
end
|
210
325
|
|
211
|
-
# this method
|
212
|
-
|
213
|
-
#
|
214
|
-
# @dispatch will be nil when this method exits
|
215
|
-
#
|
216
|
-
def stop_event_thread!
|
326
|
+
# use this method to set the @_shutting_down flag to true
|
327
|
+
def shut_down!
|
217
328
|
logger.debug { "#{self.class}##{__method__}" }
|
218
329
|
|
219
|
-
|
220
|
-
unless @_closed
|
221
|
-
wake_event_loop! # this is a C method
|
222
|
-
end
|
223
|
-
@event_thread.join
|
224
|
-
@event_thread = nil
|
225
|
-
end
|
330
|
+
@mutex.synchronize { @_shutting_down = true }
|
226
331
|
end
|
227
332
|
|
228
333
|
# called by underlying C code to signal we're running
|
229
334
|
def zkc_set_running_and_notify!
|
230
335
|
logger.debug { "#{self.class}##{__method__}" }
|
231
336
|
|
232
|
-
@
|
337
|
+
@mutex.lock
|
338
|
+
begin
|
233
339
|
@_running = true
|
234
340
|
@running_cond.broadcast
|
341
|
+
ensure
|
342
|
+
@mutex.unlock
|
235
343
|
end
|
236
344
|
end
|
237
345
|
|
238
346
|
def notify_connected!
|
239
|
-
@
|
347
|
+
@mutex.lock
|
348
|
+
begin
|
240
349
|
@connected_cond.broadcast
|
350
|
+
ensure
|
351
|
+
@mutex.unlock
|
241
352
|
end
|
242
353
|
end
|
243
354
|
end
|