zookeeper 0.9.3-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+