Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 324 lines (239 sloc) 7.214 kB
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
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
451e3ea @slyphon after a code review by Andrew Wason, a better pattern for GIL-release…
slyphon authored
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;
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
53 }
54
55 =end
56
57 REGEXP = /^ZOOAPI int (zoo_[^(]+)\(([^)]+)\);$/m
58
59 require 'forwardable'
60 require 'stringio'
61
a00c9c9 @slyphon look for zookeeper.h instead of using a hardcoded path
slyphon authored
62 THIS_DIR = File.expand_path('..', __FILE__)
63
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
64 ZKRB_WRAPPER_H_PATH = File.expand_path('../zkrb_wrapper.h', __FILE__)
65 ZKRB_WRAPPER_C_PATH = File.expand_path('../zkrb_wrapper.c', __FILE__)
66
67 # the struct that holds the call args for zoo_fn_name
68 class CallStruct
69 attr_reader :zoo_fn_name, :typed_args, :name
70
71 def initialize(zoo_fn_name, typed_args)
72 @zoo_fn_name, @typed_args = zoo_fn_name, typed_args
73 @name = "zkrb_#{zoo_fn_name}_args_t"
74 end
75
76 def body
77 @body ||= (
78 lines = ["typedef struct {"]
79 lines += typed_args.map{|n| " #{n};"}
80 lines << " int rc;"
81 lines << "} #{name};"
82 lines.join("\n")
83 )
84 end
85 end
86
87 module MemberNames
88 def member_names
89 @member_names ||= typed_args.map { |n| n.split(/[ *]/).last }
90 end
91 end
92
93 # the zkrb_gvl_zoo_* function
94 class WrapperFunction
95 extend Forwardable
96 include MemberNames
97
98 PREFIX = 'zkrb_gvl'
99
100 def_delegators :struct, :typed_args
101
102 attr_reader :struct, :name, :zoo_fn_name
103
104 def initialize(zoo_fn_name, struct)
105 @zoo_fn_name = zoo_fn_name
106 @struct = struct
107 @name = "#{PREFIX}_#{zoo_fn_name}"
108 end
109
110 def fn_signature
111 @fn_signature ||= "static VALUE #{name}(void *data)"
112 end
113
114 def body
115 @body ||= (
116 lines = ["#{fn_signature} {"]
117 lines << " #{struct.name} *a = (#{struct.name} *)data;"
118
119 funcall = " a->rc = #{zoo_fn_name}("
120 funcall << member_names.map { |m| "a->#{m}" }.join(', ')
121 funcall << ');'
122
123 lines << funcall
124
125 lines << " return Qnil;"
126 lines << "}"
127 lines.join("\n")
128 )
129 end
130 end
131
132 # the zkrb_call_zoo_* function
133 class CallingFunction
134 extend Forwardable
135 include MemberNames
136
137 PREFIX = 'zkrb_call'
138
139 def_delegators :struct, :typed_args
140
141 attr_reader :struct, :wrapper_fn, :zoo_fn_name, :name
142
143 def initialize(zoo_fn_name, struct, wrapper_fn)
144 @zoo_fn_name, @struct, @wrapper_fn = zoo_fn_name, struct, wrapper_fn
145
146 @name = "#{PREFIX}_#{zoo_fn_name}"
147 end
148
149 def fn_signature
ee7fcd9 @slyphon fix use of 'static'
slyphon authored
150 @fn_signature ||= "int #{name}(#{typed_args.join(', ')})"
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
151 end
152
451e3ea @slyphon after a code review by Andrew Wason, a better pattern for GIL-release…
slyphon authored
153 def initializer_lines
154 @initializer_lines ||= member_names.map { |n| " .#{n} = #{n}" }.join(",\n")
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
155 end
156
157 def top
158 <<-EOS
159 // wrapper that calls #{zoo_fn_name} via #{wrapper_fn.name} inside rb_thread_blocking_region
160 #{fn_signature} {
451e3ea @slyphon after a code review by Andrew Wason, a better pattern for GIL-release…
slyphon authored
161 #{struct.name} args = {
162 .rc = ZKRB_FAIL,
163 #{initializer_lines}
164 };
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
165 EOS
166 end
167
168 def rb_thread_blocking_region_call
451e3ea @slyphon after a code review by Andrew Wason, a better pattern for GIL-release…
slyphon authored
169 " zkrb_thread_blocking_region(#{wrapper_fn.name}, (void *)&args);"
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
170 end
171
172 def bottom
173 <<-EOS
174
451e3ea @slyphon after a code review by Andrew Wason, a better pattern for GIL-release…
slyphon authored
175 return args.rc;
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
176 }
177 EOS
178 end
179
180 def body
451e3ea @slyphon after a code review by Andrew Wason, a better pattern for GIL-release…
slyphon authored
181 @body ||= [top, rb_thread_blocking_region_call, bottom].join("\n")
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
182 end
183 end
184
185 class GeneratedCode < Struct.new(:structs, :wrapper_fns, :calling_fns)
186 def initialize(*a)
187 super
188
189 self.structs ||= []
190 self.wrapper_fns ||= []
191 self.calling_fns ||= []
192 end
193
194 def self.from_zookeeper_h(text)
195 new.tap do |code|
196 while true
197 break unless text =~ REGEXP
52fac08 @slyphon change where the $~.post_match happens
slyphon authored
198 text = $~.post_match
199
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
200 zoo_fn_name, argstr = $1
201 argstr = $2
202
203 typed_args = argstr.split(',').map(&:strip)
204
ee7fcd9 @slyphon fix use of 'static'
slyphon authored
205 # gah, fix up zoo_aset_acl which has a void_completion_t with no name assigned
206 if zoo_fn_name == 'zoo_aset_acl'
207 if idx = typed_args.index('void_completion_t')
208 typed_args[idx] = 'void_completion_t completion'
209 end
210 end
211
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
212 struct = CallStruct.new(zoo_fn_name, typed_args)
213 wrapper_fn = WrapperFunction.new(zoo_fn_name, struct)
214 calling_fn = CallingFunction.new(zoo_fn_name, struct, wrapper_fn)
215
216 code.structs << struct
217 code.wrapper_fns << wrapper_fn
218 code.calling_fns << calling_fn
219 end
220 end
221 end
222 end
223
224 def render_header_file(code)
ee7fcd9 @slyphon fix use of 'static'
slyphon authored
225 StringIO.new('zkrb_wrapper.h', 'w').tap do |fp|
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
226 fp.puts <<-EOS
227 #ifndef ZKRB_WRAPPER_H
228 #define ZKRB_WRAPPER_H
229 #if 0
230
231 AUTOGENERATED BY #{File.basename(__FILE__)}
232
233 #endif
234
235 #include "ruby.h"
236 #include "c-client-src/zookeeper.h"
cfec0a0 @slyphon add 1.8.7 compatibility hack (yeah, yeah, i know)...
slyphon authored
237 #include "zkrb_wrapper_compat.h"
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
238 #include "dbg.h"
239
240 #define ZKRB_FAIL -1
241
242 EOS
243
244 code.structs.each do |struct|
245 fp.puts(struct.body)
246 fp.puts
247 end
248
249 code.calling_fns.each do |cf|
250 fp.puts "#{cf.fn_signature};"
251 end
252
253 fp.puts <<-EOS
254
255 #endif /* ZKRB_WRAPPER_H */
256 EOS
257
258 end.string
259 end
260
261 def render_c_file(code)
ee7fcd9 @slyphon fix use of 'static'
slyphon authored
262 StringIO.new('zkrb_wrapper.c', 'w').tap do |fp|
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
263 fp.puts <<-EOS
264 /*
265
266 Autogenerated boilerplate wrappers around zoo_* function calls necessary for using
267 rb_thread_blocking_region to release the GIL when calling native code.
268
269 generated by ext/#{File.basename(__FILE__)}
270
271 */
272
273 #include "ruby.h"
274 #include "zkrb_wrapper.h"
275 #include <errno.h>
276 #include <stdio.h>
277 #include <stdlib.h>
278
279 EOS
280
281 code.wrapper_fns.zip(code.calling_fns) do |wrap_fn, call_fn|
ee7fcd9 @slyphon fix use of 'static'
slyphon authored
282 fp.puts "#{wrap_fn.body}\n\n"
283 fp.puts "#{call_fn.body}\n\n"
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
284 end
285
286 end.string
287 end
288
ee7fcd9 @slyphon fix use of 'static'
slyphon authored
289
290 def help!
291 $stderr.puts "usage: #{File.basename(__FILE__)} {all|headers|code}"
292 exit 1
293 end
294
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
295 def main
ee7fcd9 @slyphon fix use of 'static'
slyphon authored
296 help! if ARGV.empty?
a00c9c9 @slyphon look for zookeeper.h instead of using a hardcoded path
slyphon authored
297 opts = []
298
299 zookeeper_h_path = Dir[File.join(THIS_DIR, "**/zookeeper.h")].first
300
301 raise "Could not locate zookeeper.h!" unless zookeeper_h_path
302
303 text = File.read(zookeeper_h_path)
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
304 code = GeneratedCode.from_zookeeper_h(text)
305
ee7fcd9 @slyphon fix use of 'static'
slyphon authored
306 cmd = ARGV.first
307
308 help! unless %w[headers all code].include?(cmd)
309
310 if %w[headers all].include?(cmd)
311 $stderr.puts "writing #{ZKRB_WRAPPER_H_PATH}"
312 File.open(ZKRB_WRAPPER_H_PATH, 'w') { |fp| fp.write(render_header_file(code)) }
313 end
314
315 if %w[code all].include?(cmd)
316 $stderr.puts "writing #{ZKRB_WRAPPER_C_PATH}"
317 File.open(ZKRB_WRAPPER_C_PATH, 'w') { |fp| fp.write(render_c_file(code)) }
318 end
319
201bbeb @slyphon a pretty bare-knuckle code generation script for rb_thread_blocking_r…
slyphon authored
320 end
321
322 main
323
Something went wrong with that request. Please try again.