zookeeper 0.9.3-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +10 -0
  2. data/CHANGELOG +119 -0
  3. data/Gemfile +17 -0
  4. data/LICENSE +23 -0
  5. data/Manifest +29 -0
  6. data/README.markdown +59 -0
  7. data/Rakefile +139 -0
  8. data/examples/cloud_config.rb +125 -0
  9. data/ext/.gitignore +6 -0
  10. data/ext/Rakefile +51 -0
  11. data/ext/c_zookeeper.rb +212 -0
  12. data/ext/dbg.h +53 -0
  13. data/ext/depend +5 -0
  14. data/ext/extconf.rb +85 -0
  15. data/ext/generate_gvl_code.rb +316 -0
  16. data/ext/zkc-3.3.5.tar.gz +0 -0
  17. data/ext/zkrb_wrapper.c +731 -0
  18. data/ext/zkrb_wrapper.h +330 -0
  19. data/ext/zkrb_wrapper_compat.c +15 -0
  20. data/ext/zkrb_wrapper_compat.h +11 -0
  21. data/ext/zookeeper_base.rb +211 -0
  22. data/ext/zookeeper_c.c +725 -0
  23. data/ext/zookeeper_lib.c +677 -0
  24. data/ext/zookeeper_lib.h +172 -0
  25. data/java/zookeeper_base.rb +477 -0
  26. data/lib/zookeeper.rb +297 -0
  27. data/lib/zookeeper/acls.rb +40 -0
  28. data/lib/zookeeper/callbacks.rb +91 -0
  29. data/lib/zookeeper/common.rb +174 -0
  30. data/lib/zookeeper/common/queue_with_pipe.rb +78 -0
  31. data/lib/zookeeper/constants.rb +57 -0
  32. data/lib/zookeeper/em_client.rb +55 -0
  33. data/lib/zookeeper/exceptions.rb +100 -0
  34. data/lib/zookeeper/stat.rb +21 -0
  35. data/lib/zookeeper/version.rb +6 -0
  36. data/notes.txt +14 -0
  37. data/spec/c_zookeeper_spec.rb +50 -0
  38. data/spec/chrooted_connection_spec.rb +81 -0
  39. data/spec/default_watcher_spec.rb +41 -0
  40. data/spec/em_spec.rb +51 -0
  41. data/spec/log4j.properties +17 -0
  42. data/spec/shared/all_success_return_values.rb +10 -0
  43. data/spec/shared/connection_examples.rb +1018 -0
  44. data/spec/spec_helper.rb +119 -0
  45. data/spec/support/progress_formatter.rb +15 -0
  46. data/spec/zookeeper_spec.rb +24 -0
  47. data/test/test_basic.rb +37 -0
  48. data/test/test_callback1.rb +36 -0
  49. data/test/test_close.rb +16 -0
  50. data/test/test_esoteric.rb +7 -0
  51. data/test/test_watcher1.rb +56 -0
  52. data/test/test_watcher2.rb +52 -0
  53. metadata +181 -0
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,51 @@
1
+
2
+ task :clean do
3
+ if File.exists?('Makefile')
4
+ sh 'make clean'
5
+ else
6
+ $stderr.puts "nothing to clean, no Makefile"
7
+ end
8
+ end
9
+
10
+ GENERATE_GVL_CODE_RB = 'generate_gvl_code.rb'
11
+
12
+ file 'c' do
13
+ if tarball = Dir['zkc-*.tar.gz'].first
14
+ sh "tar -zxf #{tarball}"
15
+ else
16
+ raise "couldn't find the tarball! wtf?!"
17
+ end
18
+ end
19
+
20
+ file GENERATE_GVL_CODE_RB => 'c'
21
+
22
+ file 'zkrb_wrapper.c' => GENERATE_GVL_CODE_RB do
23
+ sh "ruby generate_gvl_code.rb code"
24
+ end
25
+
26
+ file 'zkrb_wrapper.h' => GENERATE_GVL_CODE_RB do
27
+ sh "ruby generate_gvl_code.rb headers"
28
+ end
29
+
30
+ ZKRB_WRAPPER = %w[zkrb_wrapper.c zkrb_wrapper.h]
31
+
32
+
33
+ task :wrappers => ZKRB_WRAPPER
34
+
35
+
36
+ task :clobber => :clean do
37
+ rm_rf %w[Makefile c lib bin include]
38
+ end
39
+
40
+ task :build_zkc do
41
+ sh 'ruby extconf.rb'
42
+ end
43
+
44
+ file 'Makefile' => :build_zkc
45
+
46
+ task :build => [ZKRB_WRAPPER, 'Makefile'].flatten do
47
+ sh 'make'
48
+ end
49
+
50
+ task :default => :build
51
+
@@ -0,0 +1,212 @@
1
+ require 'zookeeper/constants'
2
+ require File.expand_path('../zookeeper_c', __FILE__)
3
+
4
+ # TODO: see if we can get the destructor to handle thread/event queue teardown
5
+ # when we're garbage collected
6
+ class CZookeeper
7
+ include ZookeeperCommon
8
+ include ZookeeperConstants
9
+ include ZookeeperExceptions
10
+
11
+ DEFAULT_SESSION_TIMEOUT_MSEC = 10000
12
+
13
+ class GotNilEventException < StandardError; end
14
+
15
+ # assume we're at debug level
16
+ def self.get_debug_level
17
+ @debug_level ||= ZOO_LOG_LEVEL_INFO
18
+ end
19
+
20
+ def self.set_debug_level(value)
21
+ @debug_level = value
22
+ set_zkrb_debug_level(value)
23
+ end
24
+
25
+ def initialize(host, event_queue, opts={})
26
+ @host = host
27
+ @event_queue = event_queue
28
+
29
+ # used by the C layer. CZookeeper sets this to true when the init method
30
+ # has completed. once this is set to true, it stays true.
31
+ #
32
+ # you should grab the @start_stop_mutex before messing with this flag
33
+ @_running = nil
34
+
35
+ # This is set to true after destroy_zkrb_instance has been called and all
36
+ # CZookeeper state has been cleaned up
37
+ @_closed = false # also used by the C layer
38
+
39
+ # set by the ruby side to indicate we are in shutdown mode used by method_get_next_event
40
+ @_shutting_down = false
41
+
42
+ # the actual C data is stashed in this ivar. never *ever* touch this
43
+ @_data = nil
44
+
45
+ @_session_timeout_msec = DEFAULT_SESSION_TIMEOUT_MSEC
46
+
47
+ @start_stop_mutex = Monitor.new
48
+
49
+ # used to signal that we're running
50
+ @running_cond = @start_stop_mutex.new_cond
51
+
52
+ @event_thread = nil
53
+
54
+ setup_event_thread!
55
+
56
+ zkrb_init(@host)
57
+
58
+ logger.debug { "init returned!" }
59
+ end
60
+
61
+ def closed?
62
+ @start_stop_mutex.synchronize { !!@_closed }
63
+ end
64
+
65
+ def running?
66
+ @start_stop_mutex.synchronize { !!@_running }
67
+ end
68
+
69
+ def shutting_down?
70
+ @start_stop_mutex.synchronize { !!@_shutting_down }
71
+ end
72
+
73
+ def connected?
74
+ state == ZOO_CONNECTED_STATE
75
+ end
76
+
77
+ def connecting?
78
+ state == ZOO_CONNECTING_STATE
79
+ end
80
+
81
+ def associating?
82
+ state == ZOO_ASSOCIATING_STATE
83
+ end
84
+
85
+ def close
86
+ return if closed?
87
+
88
+ shut_down!
89
+ stop_event_thread!
90
+
91
+ @start_stop_mutex.synchronize do
92
+ if !@_closed and @_data
93
+ close_handle
94
+ end
95
+ end
96
+
97
+ nil
98
+ end
99
+
100
+ def state
101
+ return ZOO_CLOSED_STATE if closed?
102
+ zkrb_state
103
+ end
104
+
105
+ # this implementation is gross, but i don't really see another way of doing it
106
+ # without more grossness
107
+ #
108
+ # returns true if we're connected, false if we're not
109
+ #
110
+ # if timeout is nil, we never time out, and wait forever for CONNECTED state
111
+ #
112
+ def wait_until_connected(timeout=10)
113
+ return false unless wait_until_running(timeout)
114
+
115
+ time_to_stop = timeout ? (Time.now + timeout) : nil
116
+
117
+ until connected? or (time_to_stop and Time.now > time_to_stop)
118
+ Thread.pass
119
+ end
120
+
121
+ connected?
122
+ end
123
+
124
+ private
125
+ # will wait until the client has entered the running? state
126
+ # or until timeout seconds have passed.
127
+ #
128
+ # returns true if we're running, false if we timed out
129
+ def wait_until_running(timeout=5)
130
+ @start_stop_mutex.synchronize do
131
+ return true if @_running
132
+ @running_cond.wait(timeout)
133
+ !!@_running
134
+ end
135
+ end
136
+
137
+ def setup_event_thread!
138
+ @event_thread ||= Thread.new(&method(:_event_thread_body))
139
+ end
140
+
141
+ def _event_thread_body
142
+ logger.debug { "event_thread waiting until running: #{@_running}" }
143
+
144
+ @start_stop_mutex.synchronize do
145
+ @running_cond.wait_until { @_running }
146
+
147
+ if @_shutting_down
148
+ logger.error { "event thread saw @_shutting_down, bailing without entering loop" }
149
+ return
150
+ end
151
+ end
152
+
153
+ logger.debug { "event_thread running: #{@_running}" }
154
+
155
+ while true
156
+ begin
157
+ _iterate_event_delivery
158
+ rescue GotNilEventException
159
+ logger.debug { "#{self.class}##{__method__}: event delivery thread is exiting" }
160
+ break
161
+ end
162
+ end
163
+ end
164
+
165
+ def _iterate_event_delivery
166
+ get_next_event(true).tap do |hash|
167
+ raise GotNilEventException if hash.nil?
168
+ @event_queue.push(hash)
169
+ end
170
+ end
171
+
172
+ # use this method to set the @_shutting_down flag to true
173
+ def shut_down!
174
+ logger.debug { "#{self.class}##{__method__}" }
175
+
176
+ @start_stop_mutex.synchronize do
177
+ @_shutting_down = true
178
+ end
179
+ end
180
+
181
+ # this method is part of the reopen/close code, and is responsible for
182
+ # shutting down the dispatch thread.
183
+ #
184
+ # @dispatch will be nil when this method exits
185
+ #
186
+ def stop_event_thread!
187
+ logger.debug { "#{self.class}##{__method__}" }
188
+
189
+ if @event_thread
190
+ unless @_closed
191
+ wake_event_loop! # this is a C method
192
+ end
193
+ @event_thread.join
194
+ @event_thread = nil
195
+ end
196
+ end
197
+
198
+ def logger
199
+ Zookeeper.logger
200
+ end
201
+
202
+ # called by underlying C code to signal we're running
203
+ def zkc_set_running_and_notify!
204
+ logger.debug { "#{self.class}##{__method__}" }
205
+
206
+ @start_stop_mutex.synchronize do
207
+ @_running = true
208
+ @running_cond.broadcast
209
+ end
210
+ end
211
+ end
212
+
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", 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
+
data/ext/depend ADDED
@@ -0,0 +1,5 @@
1
+ zookeeper_lib.c: zookeeper_lib.h
2
+ zkrb_wrapper_compat.c: zkrb_wrapper_compat.h
3
+ zkrb_wrapper.c: zkrb_wrapper_compat.c zkrb_wrapper.h
4
+ zookeeper_c.c: zookeeper_lib.c zookeeper_lib.h zkrb_wrapper.c zkrb_wrapper.h dbg.h
5
+
data/ext/extconf.rb ADDED
@@ -0,0 +1,85 @@
1
+
2
+ require 'mkmf'
3
+ require 'rbconfig'
4
+
5
+ HERE = File.expand_path(File.dirname(__FILE__))
6
+ BUNDLE = Dir.glob("zkc-*.tar.gz").first
7
+ BUNDLE_PATH = "c"
8
+
9
+ $EXTRA_CONF = ''
10
+
11
+ # CLANG!!!! jeez, if apple would only *stop* "thinking different"
12
+ if cc = RbConfig::CONFIG['CC'] && cc =~ /^gcc/
13
+ $CC = cc
14
+ $EXTRA_CONF = "#{$EXTRA_CONF} CC=#{$CC}"
15
+ end
16
+
17
+ $CFLAGS = "#{$CFLAGS}".gsub("$(cflags)", "").gsub("-arch ppc", "")
18
+ $LDFLAGS = "#{$LDFLAGS}".gsub("$(ldflags)", "").gsub("-arch ppc", "")
19
+ $CXXFLAGS = " -std=gnu++98 #{$CFLAGS}"
20
+ $CPPFLAGS = $ARCH_FLAG = $DLDFLAGS = ""
21
+
22
+ if RUBY_VERSION == '1.8.7'
23
+ $CFLAGS << ' -DZKRB_RUBY_187'
24
+ end
25
+
26
+ ZK_DEBUG = (ENV['DEBUG'] or ARGV.any? { |arg| arg == '--debug' })
27
+ DEBUG_CFLAGS = " -O0 -ggdb3 -DHAVE_DEBUG"
28
+
29
+ if ZK_DEBUG
30
+ $stderr.puts "*** Setting debug flags. ***"
31
+ $EXTRA_CONF = "#{$EXTRA_CONF} --enable-debug"
32
+ $CFLAGS.gsub!(/ -O[^0] /, ' ')
33
+ $CFLAGS << DEBUG_CFLAGS
34
+ end
35
+
36
+ $includes = " -I#{HERE}/include"
37
+ $libraries = " -L#{HERE}/lib -L#{RbConfig::CONFIG['libdir']}"
38
+ $CFLAGS = "#{$includes} #{$libraries} #{$CFLAGS}"
39
+ $LDFLAGS = "#{$libraries} #{$LDFLAGS}"
40
+ $LIBPATH = ["#{HERE}/lib"]
41
+ $DEFLIBPATH = []
42
+
43
+ def safe_sh(cmd)
44
+ puts cmd
45
+ system(cmd)
46
+ unless $?.exited? and $?.success?
47
+ raise "command failed! #{cmd}"
48
+ end
49
+ end
50
+
51
+ Dir.chdir(HERE) do
52
+ if File.exist?("lib")
53
+ puts "Zkc already built; run 'rake clobber' in ext/ first if you need to rebuild."
54
+ else
55
+ puts "Building zkc."
56
+
57
+ unless File.exists?('c')
58
+ puts(cmd = "tar xzf #{BUNDLE} 2>&1")
59
+ raise "'#{cmd}' failed" unless system(cmd)
60
+ end
61
+
62
+ Dir.chdir(BUNDLE_PATH) do
63
+ configure = "./configure --prefix=#{HERE} --with-pic --without-cppunit --disable-dependency-tracking #{$EXTRA_CONF} 2>&1"
64
+
65
+ configure = "env CFLAGS='#{DEBUG_CFLAGS}' #{configure}" if ZK_DEBUG
66
+
67
+ safe_sh(configure)
68
+ safe_sh("make 2>&1")
69
+ safe_sh("make install 2>&1")
70
+ end
71
+
72
+ system("rm -rf #{BUNDLE_PATH}") unless ENV['DEBUG'] or ENV['DEV']
73
+ end
74
+ end
75
+
76
+ # Absolutely prevent the linker from picking up any other zookeeper_mt
77
+ Dir.chdir("#{HERE}/lib") do
78
+ system("cp -f libzookeeper_mt.a libzookeeper_mt_gem.a")
79
+ system("cp -f libzookeeper_mt.la libzookeeper_mt_gem.la")
80
+ end
81
+ $LIBS << " -lzookeeper_mt_gem"
82
+
83
+
84
+ create_makefile 'zookeeper_c'
85
+
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+ the idea here is to take each zoo_* function declaration in the header file, and turn it into
5
+ a calling arguments struct, a wrapper function and a macro for packing the values.
6
+
7
+ so for:
8
+
9
+ ZOOAPI int zoo_acreate(zhandle_t *zh, const char *path, const char *value,
10
+ int valuelen, const struct ACL_vector *acl, int flags,
11
+ string_completion_t completion, const void *data);
12
+
13
+ we want
14
+
15
+ typedef struct {
16
+ zhandle_t *zh;
17
+ const char *path;
18
+ const char *value;
19
+ int valuelen;
20
+ const struct ACL_vector *acl;
21
+ int flags;
22
+ string_completion_t completion;
23
+ const void *data;
24
+ } zkrb_zoo_acreate_args_t;
25
+
26
+ static VALUE zkrb_gvl_zoo_acreate(void *data) {
27
+ zkrb_zoo_acreate_args_t *a = (zkrb_zoo_acreate_args_t *)data;
28
+
29
+ a->rc = zoo_acreate(a->zh, a->path, a->value, a->valuelen, a->acl, a->flags, a->completion, a->data);
30
+
31
+ return Qnil;
32
+ }
33
+
34
+ static int zkrb_call_zoo_acreate(zhandle_t *zh, const char *path, const char *value,
35
+ int valuelen, const struct ACL_vector *acl, int flags,
36
+ string_completion_t completion, const void *data) {
37
+
38
+ zkrb_zoo_acreate_args_t args = {
39
+ .rc = ZKRB_FAIL,
40
+ .zh = zh,
41
+ .path = path,
42
+ .value = value,
43
+ .valuelen = valuelen,
44
+ .acl = acl,
45
+ .flags = flags,
46
+ .completion = completion,
47
+ .data = data
48
+ };
49
+
50
+ zkrb_thread_blocking_region(zkrb_gvl_zoo_acreate, (void *)&args);
51
+
52
+ return args.rc;
53
+ }
54
+
55
+ =end
56
+
57
+ REGEXP = /^ZOOAPI int (zoo_[^(]+)\(([^)]+)\);$/m
58
+
59
+ require 'forwardable'
60
+ require 'stringio'
61
+
62
+ ZKRB_WRAPPER_H_PATH = File.expand_path('../zkrb_wrapper.h', __FILE__)
63
+ ZKRB_WRAPPER_C_PATH = File.expand_path('../zkrb_wrapper.c', __FILE__)
64
+
65
+ # the struct that holds the call args for zoo_fn_name
66
+ class CallStruct
67
+ attr_reader :zoo_fn_name, :typed_args, :name
68
+
69
+ def initialize(zoo_fn_name, typed_args)
70
+ @zoo_fn_name, @typed_args = zoo_fn_name, typed_args
71
+ @name = "zkrb_#{zoo_fn_name}_args_t"
72
+ end
73
+
74
+ def body
75
+ @body ||= (
76
+ lines = ["typedef struct {"]
77
+ lines += typed_args.map{|n| " #{n};"}
78
+ lines << " int rc;"
79
+ lines << "} #{name};"
80
+ lines.join("\n")
81
+ )
82
+ end
83
+ end
84
+
85
+ module MemberNames
86
+ def member_names
87
+ @member_names ||= typed_args.map { |n| n.split(/[ *]/).last }
88
+ end
89
+ end
90
+
91
+ # the zkrb_gvl_zoo_* function
92
+ class WrapperFunction
93
+ extend Forwardable
94
+ include MemberNames
95
+
96
+ PREFIX = 'zkrb_gvl'
97
+
98
+ def_delegators :struct, :typed_args
99
+
100
+ attr_reader :struct, :name, :zoo_fn_name
101
+
102
+ def initialize(zoo_fn_name, struct)
103
+ @zoo_fn_name = zoo_fn_name
104
+ @struct = struct
105
+ @name = "#{PREFIX}_#{zoo_fn_name}"
106
+ end
107
+
108
+ def fn_signature
109
+ @fn_signature ||= "static VALUE #{name}(void *data)"
110
+ end
111
+
112
+ def body
113
+ @body ||= (
114
+ lines = ["#{fn_signature} {"]
115
+ lines << " #{struct.name} *a = (#{struct.name} *)data;"
116
+
117
+ funcall = " a->rc = #{zoo_fn_name}("
118
+ funcall << member_names.map { |m| "a->#{m}" }.join(', ')
119
+ funcall << ');'
120
+
121
+ lines << funcall
122
+
123
+ lines << " return Qnil;"
124
+ lines << "}"
125
+ lines.join("\n")
126
+ )
127
+ end
128
+ end
129
+
130
+ # the zkrb_call_zoo_* function
131
+ class CallingFunction
132
+ extend Forwardable
133
+ include MemberNames
134
+
135
+ PREFIX = 'zkrb_call'
136
+
137
+ def_delegators :struct, :typed_args
138
+
139
+ attr_reader :struct, :wrapper_fn, :zoo_fn_name, :name
140
+
141
+ def initialize(zoo_fn_name, struct, wrapper_fn)
142
+ @zoo_fn_name, @struct, @wrapper_fn = zoo_fn_name, struct, wrapper_fn
143
+
144
+ @name = "#{PREFIX}_#{zoo_fn_name}"
145
+ end
146
+
147
+ def fn_signature
148
+ @fn_signature ||= "int #{name}(#{typed_args.join(', ')})"
149
+ end
150
+
151
+ def initializer_lines
152
+ @initializer_lines ||= member_names.map { |n| " .#{n} = #{n}" }.join(",\n")
153
+ end
154
+
155
+ def top
156
+ <<-EOS
157
+ // wrapper that calls #{zoo_fn_name} via #{wrapper_fn.name} inside rb_thread_blocking_region
158
+ #{fn_signature} {
159
+ #{struct.name} args = {
160
+ .rc = ZKRB_FAIL,
161
+ #{initializer_lines}
162
+ };
163
+ EOS
164
+ end
165
+
166
+ def rb_thread_blocking_region_call
167
+ " zkrb_thread_blocking_region(#{wrapper_fn.name}, (void *)&args);"
168
+ end
169
+
170
+ def bottom
171
+ <<-EOS
172
+
173
+ return args.rc;
174
+ }
175
+ EOS
176
+ end
177
+
178
+ def body
179
+ @body ||= [top, rb_thread_blocking_region_call, bottom].join("\n")
180
+ end
181
+ end
182
+
183
+ class GeneratedCode < Struct.new(:structs, :wrapper_fns, :calling_fns)
184
+ def initialize(*a)
185
+ super
186
+
187
+ self.structs ||= []
188
+ self.wrapper_fns ||= []
189
+ self.calling_fns ||= []
190
+ end
191
+
192
+ def self.from_zookeeper_h(text)
193
+ new.tap do |code|
194
+ while true
195
+ break unless text =~ REGEXP
196
+ text = $~.post_match
197
+
198
+ zoo_fn_name, argstr = $1
199
+ argstr = $2
200
+
201
+ typed_args = argstr.split(',').map(&:strip)
202
+
203
+ # gah, fix up zoo_aset_acl which has a void_completion_t with no name assigned
204
+ if zoo_fn_name == 'zoo_aset_acl'
205
+ if idx = typed_args.index('void_completion_t')
206
+ typed_args[idx] = 'void_completion_t completion'
207
+ end
208
+ end
209
+
210
+ struct = CallStruct.new(zoo_fn_name, typed_args)
211
+ wrapper_fn = WrapperFunction.new(zoo_fn_name, struct)
212
+ calling_fn = CallingFunction.new(zoo_fn_name, struct, wrapper_fn)
213
+
214
+ code.structs << struct
215
+ code.wrapper_fns << wrapper_fn
216
+ code.calling_fns << calling_fn
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ def render_header_file(code)
223
+ StringIO.new('zkrb_wrapper.h', 'w').tap do |fp|
224
+ fp.puts <<-EOS
225
+ #ifndef ZKRB_WRAPPER_H
226
+ #define ZKRB_WRAPPER_H
227
+ #if 0
228
+
229
+ AUTOGENERATED BY #{File.basename(__FILE__)}
230
+
231
+ #endif
232
+
233
+ #include "ruby.h"
234
+ #include "c-client-src/zookeeper.h"
235
+ #include "zkrb_wrapper_compat.h"
236
+ #include "dbg.h"
237
+
238
+ #define ZKRB_FAIL -1
239
+
240
+ EOS
241
+
242
+ code.structs.each do |struct|
243
+ fp.puts(struct.body)
244
+ fp.puts
245
+ end
246
+
247
+ code.calling_fns.each do |cf|
248
+ fp.puts "#{cf.fn_signature};"
249
+ end
250
+
251
+ fp.puts <<-EOS
252
+
253
+ #endif /* ZKRB_WRAPPER_H */
254
+ EOS
255
+
256
+ end.string
257
+ end
258
+
259
+ def render_c_file(code)
260
+ StringIO.new('zkrb_wrapper.c', 'w').tap do |fp|
261
+ fp.puts <<-EOS
262
+ /*
263
+
264
+ Autogenerated boilerplate wrappers around zoo_* function calls necessary for using
265
+ rb_thread_blocking_region to release the GIL when calling native code.
266
+
267
+ generated by ext/#{File.basename(__FILE__)}
268
+
269
+ */
270
+
271
+ #include "ruby.h"
272
+ #include "zkrb_wrapper.h"
273
+ #include <errno.h>
274
+ #include <stdio.h>
275
+ #include <stdlib.h>
276
+
277
+ EOS
278
+
279
+ code.wrapper_fns.zip(code.calling_fns) do |wrap_fn, call_fn|
280
+ fp.puts "#{wrap_fn.body}\n\n"
281
+ fp.puts "#{call_fn.body}\n\n"
282
+ end
283
+
284
+ end.string
285
+ end
286
+
287
+
288
+ def help!
289
+ $stderr.puts "usage: #{File.basename(__FILE__)} {all|headers|code}"
290
+ exit 1
291
+ end
292
+
293
+ def main
294
+ help! if ARGV.empty?
295
+
296
+ text = File.read('c/include/zookeeper.h')
297
+ code = GeneratedCode.from_zookeeper_h(text)
298
+
299
+ cmd = ARGV.first
300
+
301
+ help! unless %w[headers all code].include?(cmd)
302
+
303
+ if %w[headers all].include?(cmd)
304
+ $stderr.puts "writing #{ZKRB_WRAPPER_H_PATH}"
305
+ File.open(ZKRB_WRAPPER_H_PATH, 'w') { |fp| fp.write(render_header_file(code)) }
306
+ end
307
+
308
+ if %w[code all].include?(cmd)
309
+ $stderr.puts "writing #{ZKRB_WRAPPER_C_PATH}"
310
+ File.open(ZKRB_WRAPPER_C_PATH, 'w') { |fp| fp.write(render_c_file(code)) }
311
+ end
312
+
313
+ end
314
+
315
+ main
316
+