zookeeper-ng 1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) 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/.gitignore +19 -0
  7. data/.gitmodules +3 -0
  8. data/.travis.yml +25 -0
  9. data/CHANGELOG +395 -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 +85 -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-fetch-and-add.patch +16 -0
  29. data/ext/patches/zkc-3.4.5-logging.patch +41 -0
  30. data/ext/patches/zkc-3.4.5-out-of-order-ping.patch +163 -0
  31. data/ext/patches/zkc-3.4.5-overflow.patch +11 -0
  32. data/ext/patches/zkc-3.4.5-yosemite-htonl-fix.patch +102 -0
  33. data/ext/zkc-3.4.5.tar.gz +0 -0
  34. data/ext/zkrb.c +1075 -0
  35. data/ext/zkrb_wrapper.c +775 -0
  36. data/ext/zkrb_wrapper.h +350 -0
  37. data/ext/zkrb_wrapper_compat.c +15 -0
  38. data/ext/zkrb_wrapper_compat.h +11 -0
  39. data/ext/zookeeper_base.rb +256 -0
  40. data/java/java_base.rb +503 -0
  41. data/lib/zookeeper.rb +115 -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.rb +122 -0
  47. data/lib/zookeeper/common/queue_with_pipe.rb +110 -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.rb +39 -0
  57. data/lib/zookeeper/logger/forwarding_logger.rb +84 -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/notes.txt +14 -0
  64. data/scripts/upgrade-1.0-sed-alike.rb +46 -0
  65. data/spec/c_zookeeper_spec.rb +51 -0
  66. data/spec/chrooted_connection_spec.rb +83 -0
  67. data/spec/compatibilty_spec.rb +8 -0
  68. data/spec/default_watcher_spec.rb +41 -0
  69. data/spec/em_spec.rb +51 -0
  70. data/spec/ext/zookeeper_base_spec.rb +19 -0
  71. data/spec/forked_connection_spec.rb +124 -0
  72. data/spec/latch_spec.rb +24 -0
  73. data/spec/log4j.properties +17 -0
  74. data/spec/shared/all_success_return_values.rb +10 -0
  75. data/spec/shared/connection_examples.rb +1077 -0
  76. data/spec/spec_helper.rb +61 -0
  77. data/spec/support/00_logging.rb +38 -0
  78. data/spec/support/10_spawn_zookeeper.rb +24 -0
  79. data/spec/support/progress_formatter.rb +15 -0
  80. data/spec/support/zookeeper_spec_helpers.rb +96 -0
  81. data/spec/zookeeper_spec.rb +24 -0
  82. data/zookeeper.gemspec +38 -0
  83. data/zoomonkey/duplicates +3 -0
  84. data/zoomonkey/zoomonkey.rb +194 -0
  85. metadata +157 -0
@@ -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.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 */
data/ext/dbg.h ADDED
@@ -0,0 +1,53 @@
1
+ #ifndef __dbg_h__
2
+ #define __dbg_h__
3
+
4
+ // ALL GLORY TO THE Zed A. Shaw
5
+ // http://c.learncodethehardway.org/book/learn-c-the-hard-waych21.html#x26-10500021
6
+
7
+ #include <stdio.h>
8
+ #include <errno.h>
9
+ #include <string.h>
10
+
11
+ #ifdef NDEBUG
12
+ #define debug(M, ...)
13
+ #else
14
+ #define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
15
+ #endif
16
+
17
+ #define clean_errno() (errno == 0 ? "None" : strerror(errno))
18
+
19
+ #define log_err(M, ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
20
+
21
+ #define log_warn(M, ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
22
+
23
+ #define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
24
+
25
+ // acts to assert that A is true
26
+ #define check(A, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }
27
+
28
+ // like check, but provide an explicit goto label name
29
+ #define check_goto(A, L, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto L; }
30
+
31
+ // like check, but implicit jump to 'unlock' label
32
+ #define check_unlock(A, M, ...) check_goto(A, unlock, M, ##__VA_ARGS__)
33
+
34
+ #define sentinel(M, ...) { log_err(M, ##__VA_ARGS__); errno=0; goto error; }
35
+
36
+ #define check_mem(A) check((A), "Out of memory.")
37
+
38
+ // checks the condition A, if not true, logs the message M given using zkrb_debug
39
+ // then does a goto to the label L
40
+ #define check_debug_goto(A, L, M, ...) if(!(A)) { zkrb_debug(M, ##__VA_ARGS__); errno=0; goto L; }
41
+
42
+ // check_debug_goto with implicit 'unlock' label
43
+ #define check_debug_unlock(A, M, ...) check_debug_goto(A, unlock, M, ##__VA_ARGS__)
44
+
45
+ // like check_debug_goto, but the label is implicitly 'error'
46
+ #define check_debug(A, M, ...) check_debug_goto(A, error, M, ##__VA_ARGS__)
47
+
48
+ #define zkrb_debug(M, ...) if (ZKRBDebugging) fprintf(stderr, "DEBUG %p:%s:%d: " M "\n", (void *)pthread_self(), __FILE__, __LINE__, ##__VA_ARGS__)
49
+ #define zkrb_debug_inst(O, M, ...) zkrb_debug("obj_id: %lx, " M, FIX2LONG(rb_obj_id(O)), ##__VA_ARGS__)
50
+
51
+ // __dbg_h__
52
+ #endif
53
+