alinta-ffi 1.9.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +49 -0
  3. data/LICENSE +24 -0
  4. data/README.md +112 -0
  5. data/Rakefile +243 -0
  6. data/ext/ffi_c/AbstractMemory.c +1109 -0
  7. data/ext/ffi_c/AbstractMemory.h +175 -0
  8. data/ext/ffi_c/ArrayType.c +162 -0
  9. data/ext/ffi_c/ArrayType.h +59 -0
  10. data/ext/ffi_c/Buffer.c +365 -0
  11. data/ext/ffi_c/Call.c +517 -0
  12. data/ext/ffi_c/Call.h +110 -0
  13. data/ext/ffi_c/ClosurePool.c +283 -0
  14. data/ext/ffi_c/ClosurePool.h +57 -0
  15. data/ext/ffi_c/DataConverter.c +91 -0
  16. data/ext/ffi_c/DynamicLibrary.c +339 -0
  17. data/ext/ffi_c/DynamicLibrary.h +98 -0
  18. data/ext/ffi_c/Function.c +998 -0
  19. data/ext/ffi_c/Function.h +87 -0
  20. data/ext/ffi_c/FunctionInfo.c +271 -0
  21. data/ext/ffi_c/LastError.c +184 -0
  22. data/ext/ffi_c/LastError.h +47 -0
  23. data/ext/ffi_c/LongDouble.c +63 -0
  24. data/ext/ffi_c/LongDouble.h +51 -0
  25. data/ext/ffi_c/MappedType.c +168 -0
  26. data/ext/ffi_c/MappedType.h +59 -0
  27. data/ext/ffi_c/MemoryPointer.c +197 -0
  28. data/ext/ffi_c/MemoryPointer.h +53 -0
  29. data/ext/ffi_c/MethodHandle.c +358 -0
  30. data/ext/ffi_c/MethodHandle.h +55 -0
  31. data/ext/ffi_c/Platform.c +129 -0
  32. data/ext/ffi_c/Platform.h +45 -0
  33. data/ext/ffi_c/Pointer.c +508 -0
  34. data/ext/ffi_c/Pointer.h +63 -0
  35. data/ext/ffi_c/Struct.c +829 -0
  36. data/ext/ffi_c/Struct.h +106 -0
  37. data/ext/ffi_c/StructByReference.c +190 -0
  38. data/ext/ffi_c/StructByReference.h +50 -0
  39. data/ext/ffi_c/StructByValue.c +150 -0
  40. data/ext/ffi_c/StructByValue.h +55 -0
  41. data/ext/ffi_c/StructLayout.c +698 -0
  42. data/ext/ffi_c/Thread.c +352 -0
  43. data/ext/ffi_c/Thread.h +95 -0
  44. data/ext/ffi_c/Type.c +397 -0
  45. data/ext/ffi_c/Type.h +62 -0
  46. data/ext/ffi_c/Types.c +139 -0
  47. data/ext/ffi_c/Types.h +89 -0
  48. data/ext/ffi_c/Variadic.c +304 -0
  49. data/ext/ffi_c/compat.h +78 -0
  50. data/ext/ffi_c/extconf.rb +71 -0
  51. data/ext/ffi_c/ffi.c +98 -0
  52. data/ext/ffi_c/libffi.bsd.mk +40 -0
  53. data/ext/ffi_c/libffi.darwin.mk +105 -0
  54. data/ext/ffi_c/libffi.gnu.mk +32 -0
  55. data/ext/ffi_c/libffi.mk +18 -0
  56. data/ext/ffi_c/libffi.vc.mk +26 -0
  57. data/ext/ffi_c/libffi.vc64.mk +26 -0
  58. data/ext/ffi_c/rbffi.h +57 -0
  59. data/ext/ffi_c/rbffi_endian.h +59 -0
  60. data/ext/ffi_c/win32/stdbool.h +8 -0
  61. data/ext/ffi_c/win32/stdint.h +201 -0
  62. data/ffi.gemspec +23 -0
  63. data/gen/Rakefile +30 -0
  64. data/lib/ffi.rb +20 -0
  65. data/lib/ffi/autopointer.rb +203 -0
  66. data/lib/ffi/buffer.rb +4 -0
  67. data/lib/ffi/callback.rb +4 -0
  68. data/lib/ffi/enum.rb +296 -0
  69. data/lib/ffi/errno.rb +43 -0
  70. data/lib/ffi/ffi.rb +44 -0
  71. data/lib/ffi/io.rb +62 -0
  72. data/lib/ffi/library.rb +590 -0
  73. data/lib/ffi/managedstruct.rb +84 -0
  74. data/lib/ffi/memorypointer.rb +1 -0
  75. data/lib/ffi/platform.rb +164 -0
  76. data/lib/ffi/platform/aarch64-linux/types.conf +104 -0
  77. data/lib/ffi/platform/arm-linux/types.conf +104 -0
  78. data/lib/ffi/platform/i386-cygwin/types.conf +3 -0
  79. data/lib/ffi/platform/i386-darwin/types.conf +100 -0
  80. data/lib/ffi/platform/i386-freebsd/types.conf +152 -0
  81. data/lib/ffi/platform/i386-gnu/types.conf +107 -0
  82. data/lib/ffi/platform/i386-linux/types.conf +103 -0
  83. data/lib/ffi/platform/i386-netbsd/types.conf +126 -0
  84. data/lib/ffi/platform/i386-openbsd/types.conf +128 -0
  85. data/lib/ffi/platform/i386-solaris/types.conf +122 -0
  86. data/lib/ffi/platform/i386-windows/types.conf +105 -0
  87. data/lib/ffi/platform/ia64-linux/types.conf +104 -0
  88. data/lib/ffi/platform/mips-linux/types.conf +102 -0
  89. data/lib/ffi/platform/mips64el-linux/types.conf +104 -0
  90. data/lib/ffi/platform/mipsel-linux/types.conf +102 -0
  91. data/lib/ffi/platform/powerpc-aix/types.conf +180 -0
  92. data/lib/ffi/platform/powerpc-darwin/types.conf +100 -0
  93. data/lib/ffi/platform/powerpc-linux/types.conf +100 -0
  94. data/lib/ffi/platform/powerpc64-linux/types.conf +104 -0
  95. data/lib/ffi/platform/s390-linux/types.conf +102 -0
  96. data/lib/ffi/platform/s390x-linux/types.conf +102 -0
  97. data/lib/ffi/platform/sparc-linux/types.conf +102 -0
  98. data/lib/ffi/platform/sparc-solaris/types.conf +128 -0
  99. data/lib/ffi/platform/sparc64-linux/types.conf +102 -0
  100. data/lib/ffi/platform/sparcv9-solaris/types.conf +128 -0
  101. data/lib/ffi/platform/x86_64-cygwin/types.conf +3 -0
  102. data/lib/ffi/platform/x86_64-darwin/types.conf +126 -0
  103. data/lib/ffi/platform/x86_64-freebsd/types.conf +128 -0
  104. data/lib/ffi/platform/x86_64-linux/types.conf +102 -0
  105. data/lib/ffi/platform/x86_64-netbsd/types.conf +128 -0
  106. data/lib/ffi/platform/x86_64-openbsd/types.conf +134 -0
  107. data/lib/ffi/platform/x86_64-solaris/types.conf +122 -0
  108. data/lib/ffi/platform/x86_64-windows/types.conf +120 -0
  109. data/lib/ffi/pointer.rb +161 -0
  110. data/lib/ffi/struct.rb +371 -0
  111. data/lib/ffi/struct_layout_builder.rb +227 -0
  112. data/lib/ffi/tools/const_generator.rb +229 -0
  113. data/lib/ffi/tools/generator.rb +60 -0
  114. data/lib/ffi/tools/generator_task.rb +36 -0
  115. data/lib/ffi/tools/struct_generator.rb +194 -0
  116. data/lib/ffi/tools/types_generator.rb +134 -0
  117. data/lib/ffi/types.rb +194 -0
  118. data/lib/ffi/union.rb +43 -0
  119. data/lib/ffi/variadic.rb +78 -0
  120. data/lib/ffi/version.rb +4 -0
  121. data/libtest/Benchmark.c +52 -0
  122. data/libtest/BoolTest.c +34 -0
  123. data/libtest/BufferTest.c +31 -0
  124. data/libtest/ClosureTest.c +205 -0
  125. data/libtest/EnumTest.c +51 -0
  126. data/libtest/FunctionTest.c +70 -0
  127. data/libtest/GNUmakefile +149 -0
  128. data/libtest/GlobalVariable.c +62 -0
  129. data/libtest/LastErrorTest.c +21 -0
  130. data/libtest/NumberTest.c +132 -0
  131. data/libtest/PointerTest.c +63 -0
  132. data/libtest/ReferenceTest.c +23 -0
  133. data/libtest/StringTest.c +34 -0
  134. data/libtest/StructTest.c +243 -0
  135. data/libtest/UnionTest.c +43 -0
  136. data/libtest/VariadicTest.c +99 -0
  137. data/spec/ffi/LICENSE.SPECS +22 -0
  138. data/spec/ffi/async_callback_spec.rb +35 -0
  139. data/spec/ffi/bitmask_spec.rb +575 -0
  140. data/spec/ffi/bool_spec.rb +32 -0
  141. data/spec/ffi/buffer_spec.rb +279 -0
  142. data/spec/ffi/callback_spec.rb +773 -0
  143. data/spec/ffi/custom_param_type.rb +37 -0
  144. data/spec/ffi/custom_type_spec.rb +74 -0
  145. data/spec/ffi/dup_spec.rb +52 -0
  146. data/spec/ffi/enum_spec.rb +423 -0
  147. data/spec/ffi/errno_spec.rb +20 -0
  148. data/spec/ffi/ffi_spec.rb +28 -0
  149. data/spec/ffi/fixtures/Benchmark.c +52 -0
  150. data/spec/ffi/fixtures/BitmaskTest.c +51 -0
  151. data/spec/ffi/fixtures/BoolTest.c +34 -0
  152. data/spec/ffi/fixtures/BufferTest.c +31 -0
  153. data/spec/ffi/fixtures/ClosureTest.c +205 -0
  154. data/spec/ffi/fixtures/EnumTest.c +51 -0
  155. data/spec/ffi/fixtures/FunctionTest.c +142 -0
  156. data/spec/ffi/fixtures/GNUmakefile +149 -0
  157. data/spec/ffi/fixtures/GlobalVariable.c +62 -0
  158. data/spec/ffi/fixtures/LastErrorTest.c +21 -0
  159. data/spec/ffi/fixtures/NumberTest.c +132 -0
  160. data/spec/ffi/fixtures/PipeHelper.h +21 -0
  161. data/spec/ffi/fixtures/PipeHelperPosix.c +41 -0
  162. data/spec/ffi/fixtures/PipeHelperWindows.c +72 -0
  163. data/spec/ffi/fixtures/PointerTest.c +63 -0
  164. data/spec/ffi/fixtures/ReferenceTest.c +23 -0
  165. data/spec/ffi/fixtures/StringTest.c +34 -0
  166. data/spec/ffi/fixtures/StructTest.c +243 -0
  167. data/spec/ffi/fixtures/UnionTest.c +43 -0
  168. data/spec/ffi/fixtures/VariadicTest.c +99 -0
  169. data/spec/ffi/fixtures/classes.rb +438 -0
  170. data/spec/ffi/function_spec.rb +97 -0
  171. data/spec/ffi/io_spec.rb +16 -0
  172. data/spec/ffi/library_spec.rb +286 -0
  173. data/spec/ffi/long_double.rb +30 -0
  174. data/spec/ffi/managed_struct_spec.rb +68 -0
  175. data/spec/ffi/memorypointer_spec.rb +78 -0
  176. data/spec/ffi/number_spec.rb +247 -0
  177. data/spec/ffi/platform_spec.rb +114 -0
  178. data/spec/ffi/pointer_spec.rb +285 -0
  179. data/spec/ffi/rbx/attach_function_spec.rb +34 -0
  180. data/spec/ffi/rbx/memory_pointer_spec.rb +198 -0
  181. data/spec/ffi/rbx/spec_helper.rb +6 -0
  182. data/spec/ffi/rbx/struct_spec.rb +18 -0
  183. data/spec/ffi/spec_helper.rb +93 -0
  184. data/spec/ffi/string_spec.rb +118 -0
  185. data/spec/ffi/strptr_spec.rb +50 -0
  186. data/spec/ffi/struct_by_ref_spec.rb +43 -0
  187. data/spec/ffi/struct_callback_spec.rb +69 -0
  188. data/spec/ffi/struct_initialize_spec.rb +35 -0
  189. data/spec/ffi/struct_packed_spec.rb +50 -0
  190. data/spec/ffi/struct_spec.rb +882 -0
  191. data/spec/ffi/typedef_spec.rb +91 -0
  192. data/spec/ffi/union_spec.rb +67 -0
  193. data/spec/ffi/variadic_spec.rb +132 -0
  194. data/spec/spec.opts +4 -0
  195. metadata +309 -0
@@ -0,0 +1,998 @@
1
+ /*
2
+ * Copyright (c) 2009-2011 Wayne Meissner
3
+ *
4
+ * Copyright (c) 2008-2013, Ruby FFI project contributors
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are met:
9
+ * * Redistributions of source code must retain the above copyright
10
+ * notice, this list of conditions and the following disclaimer.
11
+ * * Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ * * Neither the name of the Ruby FFI project nor the
15
+ * names of its contributors may be used to endorse or promote products
16
+ * derived from this software without specific prior written permission.
17
+ *
18
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
22
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+ */
29
+
30
+ #ifndef _MSC_VER
31
+ #include <sys/param.h>
32
+ #endif
33
+ #include <sys/types.h>
34
+ #ifndef _WIN32
35
+ # include <sys/mman.h>
36
+ # include <unistd.h>
37
+ #endif
38
+
39
+ #include <stdio.h>
40
+ #ifndef _MSC_VER
41
+ # include <stdint.h>
42
+ # include <stdbool.h>
43
+ #else
44
+ # include "win32/stdbool.h"
45
+ # if !defined(INT8_MIN)
46
+ # include "win32/stdint.h"
47
+ # endif
48
+ #endif
49
+ #include <ruby.h>
50
+
51
+ #include <ffi.h>
52
+ #if defined(HAVE_NATIVETHREAD) && !defined(_WIN32)
53
+ #include <pthread.h>
54
+ #endif
55
+ #include <fcntl.h>
56
+
57
+ #include "rbffi.h"
58
+ #include "compat.h"
59
+
60
+ #include "AbstractMemory.h"
61
+ #include "Pointer.h"
62
+ #include "Struct.h"
63
+ #include "Platform.h"
64
+ #include "Type.h"
65
+ #include "LastError.h"
66
+ #include "Call.h"
67
+ #include "ClosurePool.h"
68
+ #include "MappedType.h"
69
+ #include "Thread.h"
70
+ #include "LongDouble.h"
71
+ #include "MethodHandle.h"
72
+ #include "Function.h"
73
+
74
+ typedef struct Function_ {
75
+ Pointer base;
76
+ FunctionType* info;
77
+ MethodHandle* methodHandle;
78
+ bool autorelease;
79
+ Closure* closure;
80
+ VALUE rbProc;
81
+ VALUE rbFunctionInfo;
82
+ } Function;
83
+
84
+ static void function_mark(Function *);
85
+ static void function_free(Function *);
86
+ static VALUE function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc);
87
+ static void callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data);
88
+ static bool callback_prep(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize);
89
+ static void* callback_with_gvl(void* data);
90
+ static VALUE invoke_callback(void* data);
91
+ static VALUE save_callback_exception(void* data, VALUE exc);
92
+
93
+ #define DEFER_ASYNC_CALLBACK 1
94
+
95
+
96
+ #if defined(DEFER_ASYNC_CALLBACK)
97
+ static VALUE async_cb_event(void *);
98
+ static VALUE async_cb_call(void *);
99
+ #endif
100
+
101
+ #ifdef HAVE_RB_THREAD_CALL_WITH_GVL
102
+ extern void *rb_thread_call_with_gvl(void *(*func)(void *), void *data1);
103
+ #endif
104
+
105
+ VALUE rbffi_FunctionClass = Qnil;
106
+
107
+ #if defined(DEFER_ASYNC_CALLBACK)
108
+ static VALUE async_cb_thread = Qnil;
109
+ #endif
110
+
111
+ static ID id_call = 0, id_to_native = 0, id_from_native = 0, id_cbtable = 0, id_cb_ref = 0;
112
+
113
+ struct gvl_callback {
114
+ Closure* closure;
115
+ void* retval;
116
+ void** parameters;
117
+ bool done;
118
+ rbffi_frame_t *frame;
119
+ #if defined(DEFER_ASYNC_CALLBACK)
120
+ struct gvl_callback* next;
121
+ # ifndef _WIN32
122
+ pthread_cond_t async_cond;
123
+ pthread_mutex_t async_mutex;
124
+ # else
125
+ HANDLE async_event;
126
+ # endif
127
+ #endif
128
+ };
129
+
130
+
131
+ #if defined(DEFER_ASYNC_CALLBACK)
132
+ static struct gvl_callback* async_cb_list = NULL;
133
+ # ifndef _WIN32
134
+ static pthread_mutex_t async_cb_mutex = PTHREAD_MUTEX_INITIALIZER;
135
+ static pthread_cond_t async_cb_cond = PTHREAD_COND_INITIALIZER;
136
+ # if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
137
+ static int async_cb_pipe[2];
138
+ # endif
139
+ # else
140
+ static HANDLE async_cb_cond;
141
+ static CRITICAL_SECTION async_cb_lock;
142
+ # if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
143
+ static int async_cb_pipe[2];
144
+ # endif
145
+ # endif
146
+ #endif
147
+
148
+
149
+ static VALUE
150
+ function_allocate(VALUE klass)
151
+ {
152
+ Function *fn;
153
+ VALUE obj;
154
+
155
+ obj = Data_Make_Struct(klass, Function, function_mark, function_free, fn);
156
+
157
+ fn->base.memory.flags = MEM_RD;
158
+ fn->base.rbParent = Qnil;
159
+ fn->rbProc = Qnil;
160
+ fn->rbFunctionInfo = Qnil;
161
+ fn->autorelease = true;
162
+
163
+ return obj;
164
+ }
165
+
166
+ static void
167
+ function_mark(Function *fn)
168
+ {
169
+ rb_gc_mark(fn->base.rbParent);
170
+ rb_gc_mark(fn->rbProc);
171
+ rb_gc_mark(fn->rbFunctionInfo);
172
+ }
173
+
174
+ static void
175
+ function_free(Function *fn)
176
+ {
177
+ if (fn->methodHandle != NULL) {
178
+ rbffi_MethodHandle_Free(fn->methodHandle);
179
+ }
180
+
181
+ if (fn->closure != NULL && fn->autorelease) {
182
+ rbffi_Closure_Free(fn->closure);
183
+ }
184
+
185
+ xfree(fn);
186
+ }
187
+
188
+ /*
189
+ * @param [Type, Symbol] return_type return type for the function
190
+ * @param [Array<Type, Symbol>] param_types array of parameters types
191
+ * @param [Hash] options see {FFI::FunctionType} for available options
192
+ * @return [self]
193
+ * A new Function instance.
194
+ *
195
+ * Define a function from a Proc or a block.
196
+ *
197
+ * @overload initialize(return_type, param_types, options = {}) { |i| ... }
198
+ * @yieldparam i parameters for the function
199
+ * @overload initialize(return_type, param_types, proc, options = {})
200
+ * @param [Proc] proc
201
+ */
202
+ static VALUE
203
+ function_initialize(int argc, VALUE* argv, VALUE self)
204
+ {
205
+
206
+ VALUE rbReturnType = Qnil, rbParamTypes = Qnil, rbProc = Qnil, rbOptions = Qnil;
207
+ VALUE rbFunctionInfo = Qnil;
208
+ VALUE infoArgv[3];
209
+ int nargs;
210
+
211
+ nargs = rb_scan_args(argc, argv, "22", &rbReturnType, &rbParamTypes, &rbProc, &rbOptions);
212
+
213
+ /*
214
+ * Callback with block,
215
+ * e.g. Function.new(:int, [ :int ]) { |i| blah }
216
+ * or Function.new(:int, [ :int ], { :convention => :stdcall }) { |i| blah }
217
+ */
218
+ if (rb_block_given_p()) {
219
+ if (nargs > 3) {
220
+ rb_raise(rb_eArgError, "cannot create function with both proc/address and block");
221
+ }
222
+ rbOptions = rbProc;
223
+ rbProc = rb_block_proc();
224
+ } else {
225
+ /* Callback with proc, or Function with address
226
+ * e.g. Function.new(:int, [ :int ], Proc.new { |i| })
227
+ * Function.new(:int, [ :int ], Proc.new { |i| }, { :convention => :stdcall })
228
+ * Function.new(:int, [ :int ], addr)
229
+ * Function.new(:int, [ :int ], addr, { :convention => :stdcall })
230
+ */
231
+ }
232
+
233
+ infoArgv[0] = rbReturnType;
234
+ infoArgv[1] = rbParamTypes;
235
+ infoArgv[2] = rbOptions;
236
+ rbFunctionInfo = rb_class_new_instance(rbOptions != Qnil ? 3 : 2, infoArgv, rbffi_FunctionTypeClass);
237
+
238
+ function_init(self, rbFunctionInfo, rbProc);
239
+
240
+ return self;
241
+ }
242
+
243
+ /*
244
+ * call-seq: initialize_copy(other)
245
+ * @return [nil]
246
+ * DO NOT CALL THIS METHOD
247
+ */
248
+ static VALUE
249
+ function_initialize_copy(VALUE self, VALUE other)
250
+ {
251
+ rb_raise(rb_eRuntimeError, "cannot duplicate function instances");
252
+ return Qnil;
253
+ }
254
+
255
+ VALUE
256
+ rbffi_Function_NewInstance(VALUE rbFunctionInfo, VALUE rbProc)
257
+ {
258
+ return function_init(function_allocate(rbffi_FunctionClass), rbFunctionInfo, rbProc);
259
+ }
260
+
261
+ VALUE
262
+ rbffi_Function_ForProc(VALUE rbFunctionInfo, VALUE proc)
263
+ {
264
+ VALUE callback, cbref, cbTable;
265
+ Function* fp;
266
+
267
+ cbref = RTEST(rb_ivar_defined(proc, id_cb_ref)) ? rb_ivar_get(proc, id_cb_ref) : Qnil;
268
+ /* If the first callback reference has the same function function signature, use it */
269
+ if (cbref != Qnil && CLASS_OF(cbref) == rbffi_FunctionClass) {
270
+ Data_Get_Struct(cbref, Function, fp);
271
+ if (fp->rbFunctionInfo == rbFunctionInfo) {
272
+ return cbref;
273
+ }
274
+ }
275
+
276
+ cbTable = RTEST(rb_ivar_defined(proc, id_cbtable)) ? rb_ivar_get(proc, id_cbtable) : Qnil;
277
+ if (cbTable != Qnil && (callback = rb_hash_aref(cbTable, rbFunctionInfo)) != Qnil) {
278
+ return callback;
279
+ }
280
+
281
+ /* No existing function for the proc with that signature, create a new one and cache it */
282
+ callback = rbffi_Function_NewInstance(rbFunctionInfo, proc);
283
+ if (cbref == Qnil) {
284
+ /* If there is no other cb already cached for this proc, we can use the ivar slot */
285
+ rb_ivar_set(proc, id_cb_ref, callback);
286
+ } else {
287
+ /* The proc instance has been used as more than one type of callback, store extras in a hash */
288
+ cbTable = rb_hash_new();
289
+ rb_ivar_set(proc, id_cbtable, cbTable);
290
+ rb_hash_aset(cbTable, rbFunctionInfo, callback);
291
+ }
292
+
293
+ return callback;
294
+ }
295
+
296
+ static VALUE
297
+ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
298
+ {
299
+ Function* fn = NULL;
300
+
301
+ Data_Get_Struct(self, Function, fn);
302
+
303
+ fn->rbFunctionInfo = rbFunctionInfo;
304
+
305
+ Data_Get_Struct(fn->rbFunctionInfo, FunctionType, fn->info);
306
+
307
+ if (rb_obj_is_kind_of(rbProc, rbffi_PointerClass)) {
308
+ Pointer* orig;
309
+ Data_Get_Struct(rbProc, Pointer, orig);
310
+ fn->base.memory = orig->memory;
311
+ fn->base.rbParent = rbProc;
312
+
313
+ } else if (rb_obj_is_kind_of(rbProc, rb_cProc) || rb_respond_to(rbProc, id_call)) {
314
+ if (fn->info->closurePool == NULL) {
315
+ fn->info->closurePool = rbffi_ClosurePool_New(sizeof(ffi_closure), callback_prep, fn->info);
316
+ if (fn->info->closurePool == NULL) {
317
+ rb_raise(rb_eNoMemError, "failed to create closure pool");
318
+ }
319
+ }
320
+
321
+ #if defined(DEFER_ASYNC_CALLBACK)
322
+ if (async_cb_thread == Qnil) {
323
+ #if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) && defined(_WIN32)
324
+ _pipe(async_cb_pipe, 1024, O_BINARY);
325
+ #elif !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
326
+ pipe(async_cb_pipe);
327
+ fcntl(async_cb_pipe[0], F_SETFL, fcntl(async_cb_pipe[0], F_GETFL) | O_NONBLOCK);
328
+ fcntl(async_cb_pipe[1], F_SETFL, fcntl(async_cb_pipe[1], F_GETFL) | O_NONBLOCK);
329
+ #endif
330
+ async_cb_thread = rb_thread_create(async_cb_event, NULL);
331
+ }
332
+
333
+ #endif
334
+
335
+ fn->closure = rbffi_Closure_Alloc(fn->info->closurePool);
336
+ fn->closure->info = fn;
337
+ fn->base.memory.address = fn->closure->code;
338
+ fn->base.memory.size = sizeof(*fn->closure);
339
+ fn->autorelease = true;
340
+
341
+ } else {
342
+ rb_raise(rb_eTypeError, "wrong argument type %s, expected pointer or proc",
343
+ rb_obj_classname(rbProc));
344
+ }
345
+
346
+ fn->rbProc = rbProc;
347
+
348
+ return self;
349
+ }
350
+
351
+ /*
352
+ * call-seq: call(*args)
353
+ * @param [Array] args function arguments
354
+ * @return [FFI::Type]
355
+ * Call the function
356
+ */
357
+ static VALUE
358
+ function_call(int argc, VALUE* argv, VALUE self)
359
+ {
360
+ Function* fn;
361
+
362
+ Data_Get_Struct(self, Function, fn);
363
+
364
+ return (*fn->info->invoke)(argc, argv, fn->base.memory.address, fn->info);
365
+ }
366
+
367
+ /*
368
+ * call-seq: attach(m, name)
369
+ * @param [Module] m
370
+ * @param [String] name
371
+ * @return [self]
372
+ * Attach a Function to the Module +m+ as +name+.
373
+ */
374
+ static VALUE
375
+ function_attach(VALUE self, VALUE module, VALUE name)
376
+ {
377
+ Function* fn;
378
+ char var[1024];
379
+
380
+ Data_Get_Struct(self, Function, fn);
381
+
382
+ if (fn->info->parameterCount == -1) {
383
+ rb_raise(rb_eRuntimeError, "cannot attach variadic functions");
384
+ return Qnil;
385
+ }
386
+
387
+ if (!rb_obj_is_kind_of(module, rb_cModule)) {
388
+ rb_raise(rb_eRuntimeError, "trying to attach function to non-module");
389
+ return Qnil;
390
+ }
391
+
392
+ if (fn->methodHandle == NULL) {
393
+ fn->methodHandle = rbffi_MethodHandle_Alloc(fn->info, fn->base.memory.address);
394
+ }
395
+
396
+ /*
397
+ * Stash the Function in a module variable so it does not get garbage collected
398
+ */
399
+ snprintf(var, sizeof(var), "@@%s", StringValueCStr(name));
400
+ rb_cv_set(module, var, self);
401
+
402
+ rb_define_singleton_method(module, StringValueCStr(name),
403
+ rbffi_MethodHandle_CodeAddress(fn->methodHandle), -1);
404
+
405
+
406
+ rb_define_method(module, StringValueCStr(name),
407
+ rbffi_MethodHandle_CodeAddress(fn->methodHandle), -1);
408
+
409
+ return self;
410
+ }
411
+
412
+ /*
413
+ * call-seq: autorelease = autorelease
414
+ * @param [Boolean] autorelease
415
+ * @return [self]
416
+ * Set +autorelease+ attribute (See {Pointer}).
417
+ */
418
+ static VALUE
419
+ function_set_autorelease(VALUE self, VALUE autorelease)
420
+ {
421
+ Function* fn;
422
+
423
+ Data_Get_Struct(self, Function, fn);
424
+
425
+ fn->autorelease = RTEST(autorelease);
426
+
427
+ return self;
428
+ }
429
+
430
+ static VALUE
431
+ function_autorelease_p(VALUE self)
432
+ {
433
+ Function* fn;
434
+
435
+ Data_Get_Struct(self, Function, fn);
436
+
437
+ return fn->autorelease ? Qtrue : Qfalse;
438
+ }
439
+
440
+ /*
441
+ * call-seq: free
442
+ * @return [self]
443
+ * Free memory allocated by Function.
444
+ */
445
+ static VALUE
446
+ function_release(VALUE self)
447
+ {
448
+ Function* fn;
449
+
450
+ Data_Get_Struct(self, Function, fn);
451
+
452
+ if (fn->closure == NULL) {
453
+ rb_raise(rb_eRuntimeError, "cannot free function which was not allocated");
454
+ }
455
+
456
+ rbffi_Closure_Free(fn->closure);
457
+ fn->closure = NULL;
458
+
459
+ return self;
460
+ }
461
+
462
+ static void
463
+ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
464
+ {
465
+ struct gvl_callback cb = { 0 };
466
+
467
+ cb.closure = (Closure *) user_data;
468
+ cb.retval = retval;
469
+ cb.parameters = parameters;
470
+ cb.done = false;
471
+ cb.frame = rbffi_frame_current();
472
+
473
+ if (cb.frame != NULL) cb.frame->exc = Qnil;
474
+ if (cb.frame != NULL && cb.frame->has_gvl) {
475
+ callback_with_gvl(&cb);
476
+
477
+ #if defined(HAVE_RB_THREAD_CALL_WITH_GVL)
478
+ } else if (cb.frame != NULL) {
479
+ rb_thread_call_with_gvl(callback_with_gvl, &cb);
480
+ #endif
481
+ #if defined(DEFER_ASYNC_CALLBACK) && !defined(_WIN32)
482
+ } else {
483
+ bool empty = false;
484
+
485
+ pthread_mutex_init(&cb.async_mutex, NULL);
486
+ pthread_cond_init(&cb.async_cond, NULL);
487
+
488
+ /* Now signal the async callback thread */
489
+ pthread_mutex_lock(&async_cb_mutex);
490
+ empty = async_cb_list == NULL;
491
+ cb.next = async_cb_list;
492
+ async_cb_list = &cb;
493
+
494
+ #if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
495
+ pthread_mutex_unlock(&async_cb_mutex);
496
+ /* Only signal if the list was empty */
497
+ if (empty) {
498
+ char c;
499
+ write(async_cb_pipe[1], &c, 1);
500
+ }
501
+ #else
502
+ pthread_cond_signal(&async_cb_cond);
503
+ pthread_mutex_unlock(&async_cb_mutex);
504
+ #endif
505
+
506
+ /* Wait for the thread executing the ruby callback to signal it is done */
507
+ pthread_mutex_lock(&cb.async_mutex);
508
+ while (!cb.done) {
509
+ pthread_cond_wait(&cb.async_cond, &cb.async_mutex);
510
+ }
511
+ pthread_mutex_unlock(&cb.async_mutex);
512
+ pthread_cond_destroy(&cb.async_cond);
513
+ pthread_mutex_destroy(&cb.async_mutex);
514
+
515
+ #elif defined(DEFER_ASYNC_CALLBACK) && defined(_WIN32)
516
+ } else {
517
+ bool empty = false;
518
+
519
+ cb.async_event = CreateEvent(NULL, FALSE, FALSE, NULL);
520
+
521
+ /* Now signal the async callback thread */
522
+ EnterCriticalSection(&async_cb_lock);
523
+ empty = async_cb_list == NULL;
524
+ cb.next = async_cb_list;
525
+ async_cb_list = &cb;
526
+ LeaveCriticalSection(&async_cb_lock);
527
+
528
+ #if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
529
+ /* Only signal if the list was empty */
530
+ if (empty) {
531
+ char c;
532
+ write(async_cb_pipe[1], &c, 1);
533
+ }
534
+ #else
535
+ SetEvent(async_cb_cond);
536
+ #endif
537
+
538
+ /* Wait for the thread executing the ruby callback to signal it is done */
539
+ WaitForSingleObject(cb.async_event, INFINITE);
540
+ CloseHandle(cb.async_event);
541
+ #endif
542
+ }
543
+ }
544
+
545
+ #if defined(DEFER_ASYNC_CALLBACK)
546
+ struct async_wait {
547
+ void* cb;
548
+ bool stop;
549
+ };
550
+
551
+ static VALUE async_cb_wait(void *);
552
+ static void async_cb_stop(void *);
553
+
554
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
555
+ static VALUE
556
+ async_cb_event(void* unused)
557
+ {
558
+ struct async_wait w = { 0 };
559
+
560
+ w.stop = false;
561
+ while (!w.stop) {
562
+ #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
563
+ rb_thread_call_without_gvl(async_cb_wait, &w, async_cb_stop, &w);
564
+ #else
565
+ rb_thread_blocking_region(async_cb_wait, &w, async_cb_stop, &w);
566
+ #endif
567
+ if (w.cb != NULL) {
568
+ /* Start up a new ruby thread to run the ruby callback */
569
+ rb_thread_create(async_cb_call, w.cb);
570
+ }
571
+ }
572
+
573
+ return Qnil;
574
+ }
575
+
576
+ #elif defined(_WIN32)
577
+ static VALUE
578
+ async_cb_event(void* unused)
579
+ {
580
+ while (true) {
581
+ struct gvl_callback* cb;
582
+ char buf[64];
583
+ fd_set rfds;
584
+
585
+ FD_ZERO(&rfds);
586
+ FD_SET(async_cb_pipe[0], &rfds);
587
+ rb_thread_select(async_cb_pipe[0] + 1, &rfds, NULL, NULL, NULL);
588
+ read(async_cb_pipe[0], buf, sizeof(buf));
589
+
590
+ EnterCriticalSection(&async_cb_lock);
591
+ cb = async_cb_list;
592
+ async_cb_list = NULL;
593
+ LeaveCriticalSection(&async_cb_lock);
594
+
595
+ while (cb != NULL) {
596
+ struct gvl_callback* next = cb->next;
597
+ /* Start up a new ruby thread to run the ruby callback */
598
+ rb_thread_create(async_cb_call, cb);
599
+ cb = next;
600
+ }
601
+ }
602
+
603
+ return Qnil;
604
+ }
605
+ #else
606
+ static VALUE
607
+ async_cb_event(void* unused)
608
+ {
609
+ while (true) {
610
+ struct gvl_callback* cb;
611
+ char buf[64];
612
+
613
+ if (read(async_cb_pipe[0], buf, sizeof(buf)) < 0) {
614
+ rb_thread_wait_fd(async_cb_pipe[0]);
615
+ while (read(async_cb_pipe[0], buf, sizeof (buf)) < 0) {
616
+ if (rb_io_wait_readable(async_cb_pipe[0]) != Qtrue) {
617
+ return Qfalse;
618
+ }
619
+ }
620
+ }
621
+
622
+ pthread_mutex_lock(&async_cb_mutex);
623
+ cb = async_cb_list;
624
+ async_cb_list = NULL;
625
+ pthread_mutex_unlock(&async_cb_mutex);
626
+
627
+ while (cb != NULL) {
628
+ struct gvl_callback* next = cb->next;
629
+ /* Start up a new ruby thread to run the ruby callback */
630
+ rb_thread_create(async_cb_call, cb);
631
+ cb = next;
632
+ }
633
+ }
634
+
635
+ return Qnil;
636
+ }
637
+ #endif
638
+
639
+ #ifdef _WIN32
640
+ static VALUE
641
+ async_cb_wait(void *data)
642
+ {
643
+ struct async_wait* w = (struct async_wait *) data;
644
+
645
+ w->cb = NULL;
646
+
647
+ EnterCriticalSection(&async_cb_lock);
648
+
649
+ while (!w->stop && async_cb_list == NULL) {
650
+ LeaveCriticalSection(&async_cb_lock);
651
+ WaitForSingleObject(async_cb_cond, INFINITE);
652
+ EnterCriticalSection(&async_cb_lock);
653
+ }
654
+
655
+ if (async_cb_list != NULL) {
656
+ w->cb = async_cb_list;
657
+ async_cb_list = async_cb_list->next;
658
+ }
659
+
660
+ LeaveCriticalSection(&async_cb_lock);
661
+
662
+ return Qnil;
663
+ }
664
+
665
+ static void
666
+ async_cb_stop(void *data)
667
+ {
668
+ struct async_wait* w = (struct async_wait *) data;
669
+
670
+ EnterCriticalSection(&async_cb_lock);
671
+ w->stop = true;
672
+ LeaveCriticalSection(&async_cb_lock);
673
+ SetEvent(async_cb_cond);
674
+ }
675
+
676
+ #else
677
+ static VALUE
678
+ async_cb_wait(void *data)
679
+ {
680
+ struct async_wait* w = (struct async_wait *) data;
681
+
682
+ w->cb = NULL;
683
+
684
+ pthread_mutex_lock(&async_cb_mutex);
685
+
686
+ while (!w->stop && async_cb_list == NULL) {
687
+ pthread_cond_wait(&async_cb_cond, &async_cb_mutex);
688
+ }
689
+
690
+ if (async_cb_list != NULL) {
691
+ w->cb = async_cb_list;
692
+ async_cb_list = async_cb_list->next;
693
+ }
694
+
695
+ pthread_mutex_unlock(&async_cb_mutex);
696
+
697
+ return Qnil;
698
+ }
699
+
700
+ static void
701
+ async_cb_stop(void *data)
702
+ {
703
+ struct async_wait* w = (struct async_wait *) data;
704
+
705
+ pthread_mutex_lock(&async_cb_mutex);
706
+ w->stop = true;
707
+ pthread_cond_signal(&async_cb_cond);
708
+ pthread_mutex_unlock(&async_cb_mutex);
709
+ }
710
+ #endif
711
+
712
+ static VALUE
713
+ async_cb_call(void *data)
714
+ {
715
+ struct gvl_callback* cb = (struct gvl_callback *) data;
716
+
717
+ callback_with_gvl(data);
718
+
719
+ /* Signal the original native thread that the ruby code has completed */
720
+ #ifdef _WIN32
721
+ SetEvent(cb->async_event);
722
+ #else
723
+ pthread_mutex_lock(&cb->async_mutex);
724
+ cb->done = true;
725
+ pthread_cond_signal(&cb->async_cond);
726
+ pthread_mutex_unlock(&cb->async_mutex);
727
+ #endif
728
+
729
+ return Qnil;
730
+ }
731
+
732
+ #endif
733
+
734
+ static void *
735
+ callback_with_gvl(void* data)
736
+ {
737
+ rb_rescue2(invoke_callback, (VALUE) data, save_callback_exception, (VALUE) data, rb_eException, (VALUE) 0);
738
+ return NULL;
739
+ }
740
+
741
+ static VALUE
742
+ invoke_callback(void* data)
743
+ {
744
+ struct gvl_callback* cb = (struct gvl_callback *) data;
745
+
746
+ Function* fn = (Function *) cb->closure->info;
747
+ FunctionType *cbInfo = fn->info;
748
+ Type* returnType = cbInfo->returnType;
749
+ void* retval = cb->retval;
750
+ void** parameters = cb->parameters;
751
+ VALUE* rbParams;
752
+ VALUE rbReturnType = cbInfo->rbReturnType;
753
+ VALUE rbReturnValue;
754
+ int i;
755
+
756
+ rbParams = ALLOCA_N(VALUE, cbInfo->parameterCount);
757
+ for (i = 0; i < cbInfo->parameterCount; ++i) {
758
+ VALUE param;
759
+ Type* paramType = cbInfo->parameterTypes[i];
760
+ VALUE rbParamType = rb_ary_entry(cbInfo->rbParameterTypes, i);
761
+
762
+ if (unlikely(paramType->nativeType == NATIVE_MAPPED)) {
763
+ rbParamType = ((MappedType *) paramType)->rbType;
764
+ paramType = ((MappedType *) paramType)->type;
765
+ }
766
+
767
+ switch (paramType->nativeType) {
768
+ case NATIVE_INT8:
769
+ param = INT2NUM(*(int8_t *) parameters[i]);
770
+ break;
771
+ case NATIVE_UINT8:
772
+ param = UINT2NUM(*(uint8_t *) parameters[i]);
773
+ break;
774
+ case NATIVE_INT16:
775
+ param = INT2NUM(*(int16_t *) parameters[i]);
776
+ break;
777
+ case NATIVE_UINT16:
778
+ param = UINT2NUM(*(uint16_t *) parameters[i]);
779
+ break;
780
+ case NATIVE_INT32:
781
+ param = INT2NUM(*(int32_t *) parameters[i]);
782
+ break;
783
+ case NATIVE_UINT32:
784
+ param = UINT2NUM(*(uint32_t *) parameters[i]);
785
+ break;
786
+ case NATIVE_INT64:
787
+ param = LL2NUM(*(int64_t *) parameters[i]);
788
+ break;
789
+ case NATIVE_UINT64:
790
+ param = ULL2NUM(*(uint64_t *) parameters[i]);
791
+ break;
792
+ case NATIVE_LONG:
793
+ param = LONG2NUM(*(long *) parameters[i]);
794
+ break;
795
+ case NATIVE_ULONG:
796
+ param = ULONG2NUM(*(unsigned long *) parameters[i]);
797
+ break;
798
+ case NATIVE_FLOAT32:
799
+ param = rb_float_new(*(float *) parameters[i]);
800
+ break;
801
+ case NATIVE_FLOAT64:
802
+ param = rb_float_new(*(double *) parameters[i]);
803
+ break;
804
+ case NATIVE_LONGDOUBLE:
805
+ param = rbffi_longdouble_new(*(long double *) parameters[i]);
806
+ break;
807
+ case NATIVE_STRING:
808
+ param = (*(void **) parameters[i] != NULL) ? rb_tainted_str_new2(*(char **) parameters[i]) : Qnil;
809
+ break;
810
+ case NATIVE_POINTER:
811
+ param = rbffi_Pointer_NewInstance(*(void **) parameters[i]);
812
+ break;
813
+ case NATIVE_BOOL:
814
+ param = (*(uint8_t *) parameters[i]) ? Qtrue : Qfalse;
815
+ break;
816
+
817
+ case NATIVE_FUNCTION:
818
+ case NATIVE_CALLBACK:
819
+ case NATIVE_STRUCT:
820
+ param = rbffi_NativeValue_ToRuby(paramType, rbParamType, parameters[i]);
821
+ break;
822
+
823
+ default:
824
+ param = Qnil;
825
+ break;
826
+ }
827
+
828
+ /* Convert the native value into a custom ruby value */
829
+ if (unlikely(cbInfo->parameterTypes[i]->nativeType == NATIVE_MAPPED)) {
830
+ VALUE values[] = { param, Qnil };
831
+ param = rb_funcall2(((MappedType *) cbInfo->parameterTypes[i])->rbConverter, id_from_native, 2, values);
832
+ }
833
+
834
+ rbParams[i] = param;
835
+ }
836
+
837
+ rbReturnValue = rb_funcall2(fn->rbProc, id_call, cbInfo->parameterCount, rbParams);
838
+
839
+ if (unlikely(returnType->nativeType == NATIVE_MAPPED)) {
840
+ VALUE values[] = { rbReturnValue, Qnil };
841
+ rbReturnValue = rb_funcall2(((MappedType *) returnType)->rbConverter, id_to_native, 2, values);
842
+ rbReturnType = ((MappedType *) returnType)->rbType;
843
+ returnType = ((MappedType* ) returnType)->type;
844
+ }
845
+
846
+ if (rbReturnValue == Qnil || TYPE(rbReturnValue) == T_NIL) {
847
+ memset(retval, 0, returnType->ffiType->size);
848
+ } else switch (returnType->nativeType) {
849
+ case NATIVE_INT8:
850
+ case NATIVE_INT16:
851
+ case NATIVE_INT32:
852
+ *((ffi_sarg *) retval) = NUM2INT(rbReturnValue);
853
+ break;
854
+ case NATIVE_UINT8:
855
+ case NATIVE_UINT16:
856
+ case NATIVE_UINT32:
857
+ *((ffi_arg *) retval) = NUM2UINT(rbReturnValue);
858
+ break;
859
+ case NATIVE_INT64:
860
+ *((int64_t *) retval) = NUM2LL(rbReturnValue);
861
+ break;
862
+ case NATIVE_UINT64:
863
+ *((uint64_t *) retval) = NUM2ULL(rbReturnValue);
864
+ break;
865
+ case NATIVE_LONG:
866
+ *((ffi_sarg *) retval) = NUM2LONG(rbReturnValue);
867
+ break;
868
+ case NATIVE_ULONG:
869
+ *((ffi_arg *) retval) = NUM2ULONG(rbReturnValue);
870
+ break;
871
+ case NATIVE_FLOAT32:
872
+ *((float *) retval) = (float) NUM2DBL(rbReturnValue);
873
+ break;
874
+ case NATIVE_FLOAT64:
875
+ *((double *) retval) = NUM2DBL(rbReturnValue);
876
+ break;
877
+ case NATIVE_POINTER:
878
+ if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_PointerClass)) {
879
+ *((void **) retval) = ((AbstractMemory *) DATA_PTR(rbReturnValue))->address;
880
+ } else {
881
+ /* Default to returning NULL if not a value pointer object. handles nil case as well */
882
+ *((void **) retval) = NULL;
883
+ }
884
+ break;
885
+
886
+ case NATIVE_BOOL:
887
+ *((ffi_arg *) retval) = rbReturnValue == Qtrue;
888
+ break;
889
+
890
+ case NATIVE_FUNCTION:
891
+ case NATIVE_CALLBACK:
892
+ if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_PointerClass)) {
893
+
894
+ *((void **) retval) = ((AbstractMemory *) DATA_PTR(rbReturnValue))->address;
895
+
896
+ } else if (rb_obj_is_kind_of(rbReturnValue, rb_cProc) || rb_respond_to(rbReturnValue, id_call)) {
897
+ VALUE function;
898
+
899
+ function = rbffi_Function_ForProc(rbReturnType, rbReturnValue);
900
+
901
+ *((void **) retval) = ((AbstractMemory *) DATA_PTR(function))->address;
902
+ } else {
903
+ *((void **) retval) = NULL;
904
+ }
905
+ break;
906
+
907
+ case NATIVE_STRUCT:
908
+ if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_StructClass)) {
909
+ AbstractMemory* memory = ((Struct *) DATA_PTR(rbReturnValue))->pointer;
910
+
911
+ if (memory->address != NULL) {
912
+ memcpy(retval, memory->address, returnType->ffiType->size);
913
+
914
+ } else {
915
+ memset(retval, 0, returnType->ffiType->size);
916
+ }
917
+
918
+ } else {
919
+ memset(retval, 0, returnType->ffiType->size);
920
+ }
921
+ break;
922
+
923
+ default:
924
+ *((ffi_arg *) retval) = 0;
925
+ break;
926
+ }
927
+
928
+ return Qnil;
929
+ }
930
+
931
+ static VALUE
932
+ save_callback_exception(void* data, VALUE exc)
933
+ {
934
+ struct gvl_callback* cb = (struct gvl_callback *) data;
935
+
936
+ memset(cb->retval, 0, ((Function *) cb->closure->info)->info->returnType->ffiType->size);
937
+ if (cb->frame != NULL) cb->frame->exc = exc;
938
+
939
+ return Qnil;
940
+ }
941
+
942
+ static bool
943
+ callback_prep(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize)
944
+ {
945
+ FunctionType* fnInfo = (FunctionType *) ctx;
946
+ ffi_status ffiStatus;
947
+
948
+ ffiStatus = ffi_prep_closure(code, &fnInfo->ffi_cif, callback_invoke, closure);
949
+ if (ffiStatus != FFI_OK) {
950
+ snprintf(errmsg, errmsgsize, "ffi_prep_closure failed. status=%#x", ffiStatus);
951
+ return false;
952
+ }
953
+
954
+ return true;
955
+ }
956
+
957
+ void
958
+ rbffi_Function_Init(VALUE moduleFFI)
959
+ {
960
+ rbffi_FunctionInfo_Init(moduleFFI);
961
+ /*
962
+ * Document-class: FFI::Function < FFI::Pointer
963
+ */
964
+ rbffi_FunctionClass = rb_define_class_under(moduleFFI, "Function", rbffi_PointerClass);
965
+
966
+ rb_global_variable(&rbffi_FunctionClass);
967
+ rb_define_alloc_func(rbffi_FunctionClass, function_allocate);
968
+
969
+ rb_define_method(rbffi_FunctionClass, "initialize", function_initialize, -1);
970
+ rb_define_method(rbffi_FunctionClass, "initialize_copy", function_initialize_copy, 1);
971
+ rb_define_method(rbffi_FunctionClass, "call", function_call, -1);
972
+ rb_define_method(rbffi_FunctionClass, "attach", function_attach, 2);
973
+ rb_define_method(rbffi_FunctionClass, "free", function_release, 0);
974
+ rb_define_method(rbffi_FunctionClass, "autorelease=", function_set_autorelease, 1);
975
+ /*
976
+ * call-seq: autorelease
977
+ * @return [Boolean]
978
+ * Get +autorelease+ attribute.
979
+ * Synonymous for {#autorelease?}.
980
+ */
981
+ rb_define_method(rbffi_FunctionClass, "autorelease", function_autorelease_p, 0);
982
+ /*
983
+ * call-seq: autorelease?
984
+ * @return [Boolean] +autorelease+ attribute
985
+ * Get +autorelease+ attribute.
986
+ */
987
+ rb_define_method(rbffi_FunctionClass, "autorelease?", function_autorelease_p, 0);
988
+
989
+ id_call = rb_intern("call");
990
+ id_cbtable = rb_intern("@__ffi_callback_table__");
991
+ id_cb_ref = rb_intern("@__ffi_callback__");
992
+ id_to_native = rb_intern("to_native");
993
+ id_from_native = rb_intern("from_native");
994
+ #if defined(_WIN32)
995
+ InitializeCriticalSection(&async_cb_lock);
996
+ async_cb_cond = CreateEvent(NULL, FALSE, FALSE, NULL);
997
+ #endif
998
+ }