ffi 1.1.6.pre2 → 1.2.0.dev
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ffi might be problematic. Click here for more details.
- data/README.md +104 -0
- data/Rakefile +36 -43
- data/ext/ffi_c/AbstractMemory.c +22 -16
- data/ext/ffi_c/AbstractMemory.h +1 -1
- data/ext/ffi_c/Buffer.c +2 -2
- data/ext/ffi_c/Call.c +8 -6
- data/ext/ffi_c/ClosurePool.c +1 -1
- data/ext/ffi_c/DynamicLibrary.c +2 -2
- data/ext/ffi_c/Function.c +25 -26
- data/ext/ffi_c/Function.h +1 -1
- data/ext/ffi_c/MappedType.c +1 -0
- data/ext/ffi_c/MemoryPointer.c +8 -2
- data/ext/ffi_c/MethodHandle.c +1 -4
- data/ext/ffi_c/Pointer.c +1 -1
- data/ext/ffi_c/Struct.c +5 -5
- data/ext/ffi_c/StructByValue.c +1 -1
- data/ext/ffi_c/StructLayout.c +5 -5
- data/ext/ffi_c/Thread.c +2 -2
- data/ext/ffi_c/Type.c +3 -3
- data/ext/ffi_c/Types.c +2 -4
- data/ext/ffi_c/Types.h +5 -0
- data/ext/ffi_c/Variadic.c +2 -4
- data/ext/ffi_c/extconf.rb +6 -7
- data/ext/ffi_c/ffi.c +1 -1
- data/ext/ffi_c/libffi.darwin.mk +2 -2
- data/ext/ffi_c/libffi.mk +1 -1
- data/ext/ffi_c/rbffi_endian.h +1 -10
- data/spec/ffi/rbx/memory_pointer_spec.rb +0 -4
- metadata +107 -126
- data/README.rdoc +0 -102
- data/gen/log +0 -1
- data/tasks/ann.rake +0 -80
- data/tasks/extension.rake +0 -32
- data/tasks/gem.rake +0 -199
- data/tasks/git.rake +0 -41
- data/tasks/notes.rake +0 -27
- data/tasks/post_load.rake +0 -34
- data/tasks/rdoc.rake +0 -50
- data/tasks/rubyforge.rake +0 -55
- data/tasks/setup.rb +0 -301
- data/tasks/spec.rake +0 -54
- data/tasks/svn.rake +0 -47
- data/tasks/test.rake +0 -40
- data/tasks/yard.rake +0 -11
data/README.md
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# ruby-ffi http://wiki.github.com/ffi/ffi
|
2
|
+
|
3
|
+
## Description
|
4
|
+
|
5
|
+
Ruby-FFI is a ruby extension for programmatically loading dynamic
|
6
|
+
libraries, binding functions within them, and calling those functions
|
7
|
+
from Ruby code. Moreover, a Ruby-FFI extension works without changes
|
8
|
+
on Ruby and JRuby. Discover why should you write your next extension
|
9
|
+
using Ruby-FFI [here](http://wiki.github.com/ffi/ffi/why-use-ffi).
|
10
|
+
|
11
|
+
## Features/problems
|
12
|
+
|
13
|
+
* Intuitive DSL
|
14
|
+
* Supports all C native types
|
15
|
+
* C structs (also nested), enums and global variables
|
16
|
+
* Callbacks from C to ruby
|
17
|
+
* Automatic garbage collection of native memory
|
18
|
+
|
19
|
+
## Synopsis
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'ffi'
|
23
|
+
|
24
|
+
module MyLib
|
25
|
+
extend FFI::Library
|
26
|
+
ffi_lib 'c'
|
27
|
+
attach_function :puts, [ :string ], :int
|
28
|
+
end
|
29
|
+
|
30
|
+
MyLib.puts 'Hello, World using libc!'
|
31
|
+
```
|
32
|
+
|
33
|
+
For less minimalistic and more sane examples you may look at:
|
34
|
+
|
35
|
+
* the samples/ folder
|
36
|
+
* the examples on the [wiki](http://wiki.github.com/ffi/ffi)
|
37
|
+
* the projects using FFI listed on this page (http://wiki.github.com/ffi/ffi/projects-using-ffi)
|
38
|
+
|
39
|
+
## Requirements
|
40
|
+
|
41
|
+
* You need a sane building environment in order to compile the extension.
|
42
|
+
|
43
|
+
## Installation
|
44
|
+
|
45
|
+
From rubygems:
|
46
|
+
|
47
|
+
[sudo] gem install ffi
|
48
|
+
|
49
|
+
or from the git repository on github:
|
50
|
+
|
51
|
+
git clone git://github.com/ffi/ffi.git
|
52
|
+
cd ffi
|
53
|
+
rake gem:install
|
54
|
+
|
55
|
+
## License
|
56
|
+
|
57
|
+
See LICENSE file.
|
58
|
+
|
59
|
+
## Credits
|
60
|
+
|
61
|
+
The following people have submitted code, bug reports, or otherwide contributed to the success of this project:
|
62
|
+
|
63
|
+
* Alban Peignier <alban.peignier@free.fr>
|
64
|
+
* Aman Gupta <aman@tmm1.net>
|
65
|
+
* Andrea Fazzi <andrea.fazzi@alcacoop.it>
|
66
|
+
* Andreas Niederl <rico32@gmx.net>
|
67
|
+
* Andrew Cholakian <andrew@andrewvc.com>
|
68
|
+
* Antonio Terceiro <terceiro@softwarelivre.org>
|
69
|
+
* Brian Candler <B.Candler@pobox.com>
|
70
|
+
* Brian D. Burns <burns180@gmail.com>
|
71
|
+
* Bryan Kearney <bkearney@redhat.com>
|
72
|
+
* Charlie Savage <cfis@zerista.com>
|
73
|
+
* Chikanaga Tomoyuki <nagachika00@gmail.com>
|
74
|
+
* Hongli Lai <hongli@phusion.nl>
|
75
|
+
* Ian MacLeod <ian@nevir.net>
|
76
|
+
* Jake Douglas <jake@shiftedlabs.com>
|
77
|
+
* Jean-Dominique Morani <jdmorani@mac.com>
|
78
|
+
* Jeremy Hinegardner <jeremy@hinegardner.org>
|
79
|
+
* Jesús García Sáez <blaxter@gmail.com>
|
80
|
+
* Joe Khoobyar <joe@ankhcraft.com>
|
81
|
+
* Jurij Smakov <jurij@wooyd.org>
|
82
|
+
* KISHIMOTO, Makoto <ksmakoto@dd.iij4u.or.jp>
|
83
|
+
* Kim Burgestrand <kim@burgestrand.se>
|
84
|
+
* Lars Kanis <kanis@comcard.de>
|
85
|
+
* Luc Heinrich <luc@honk-honk.com>
|
86
|
+
* Luis Lavena <luislavena@gmail.com>
|
87
|
+
* Matijs van Zuijlen <matijs@matijs.net>
|
88
|
+
* Matthew King <automatthew@gmail.com>
|
89
|
+
* Mike Dalessio <mike.dalessio@gmail.com>
|
90
|
+
* NARUSE, Yui <naruse@airemix.jp>
|
91
|
+
* Park Heesob <phasis@gmail.com>
|
92
|
+
* Shin Yee <shinyee@speedgocomputing.com>
|
93
|
+
* Stephen Bannasch <stephen.bannasch@gmail.com>
|
94
|
+
* Suraj N. Kurapati <sunaku@gmail.com>
|
95
|
+
* Sylvain Daubert <sylvain.daubert@laposte.net>
|
96
|
+
* Victor Costan
|
97
|
+
* beoran@gmail.com
|
98
|
+
* ctide <christide@christide.com>
|
99
|
+
* emboss <Martin.Bosslet@googlemail.com>
|
100
|
+
* hobophobe <unusualtears@gmail.com>
|
101
|
+
* meh <meh@paranoici.org>
|
102
|
+
* postmodern <postmodern.mod3@gmail.com>
|
103
|
+
* wycats@gmail.com <wycats@gmail.com>
|
104
|
+
* Wayne Meissner <wmeissner@gmail.com>
|
data/Rakefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'rubygems/package_task'
|
2
3
|
require 'rbconfig'
|
3
4
|
|
4
5
|
USE_RAKE_COMPILER = (RUBY_PLATFORM =~ /java/) ? false : true
|
@@ -11,7 +12,6 @@ require 'date'
|
|
11
12
|
require 'fileutils'
|
12
13
|
require 'rbconfig'
|
13
14
|
|
14
|
-
load 'tasks/setup.rb'
|
15
15
|
|
16
16
|
LIBEXT = case RbConfig::CONFIG['host_os'].downcase
|
17
17
|
when /darwin/
|
@@ -64,7 +64,7 @@ OS = case RbConfig::CONFIG['host_os'].downcase
|
|
64
64
|
RbConfig::CONFIG['host_os'].downcase
|
65
65
|
end
|
66
66
|
|
67
|
-
CC=ENV['CC'] || RbConfig::CONFIG['CC'] || "gcc"
|
67
|
+
CC = ENV['CC'] || RbConfig::CONFIG['CC'] || "gcc"
|
68
68
|
|
69
69
|
GMAKE = system('which gmake >/dev/null') && 'gmake' || 'make'
|
70
70
|
|
@@ -72,47 +72,13 @@ LIBTEST = "build/libtest.#{LIBEXT}"
|
|
72
72
|
BUILD_DIR = "build"
|
73
73
|
BUILD_EXT_DIR = File.join(BUILD_DIR, "#{RbConfig::CONFIG['arch']}", 'ffi_c', RUBY_VERSION)
|
74
74
|
|
75
|
-
|
76
|
-
PROJ.name = 'ffi'
|
77
|
-
PROJ.authors = 'Wayne Meissner'
|
78
|
-
PROJ.email = 'wmeissner@gmail.com'
|
79
|
-
PROJ.url = 'http://wiki.github.com/ffi/ffi'
|
80
|
-
PROJ.version = '1.1.6.pre2'
|
81
|
-
PROJ.rubyforge.name = 'ffi'
|
82
|
-
PROJ.readme_file = 'README.rdoc'
|
75
|
+
gem_spec = eval(IO.read('ffi.gemspec'))
|
83
76
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
PROJ.ann.email[:server] = 'smtp.gmail.com'
|
90
|
-
|
91
|
-
# Gem specifications
|
92
|
-
PROJ.gem.need_tar = false
|
93
|
-
PROJ.gem.files = %w(History.txt LICENSE README.rdoc Rakefile) + Dir.glob("{ext,gen,lib,spec,tasks}/**/*")
|
94
|
-
PROJ.gem.platform = Gem::Platform::RUBY
|
95
|
-
#PROJ.gem.required_ruby_version = ">= 1.9.2"
|
96
|
-
|
97
|
-
# Override Mr. Bones autogenerated extensions and force ours in
|
98
|
-
PROJ.gem.extras['extensions'] = %w(ext/ffi_c/extconf.rb)
|
99
|
-
#PROJ.gem.extras['required_ruby_version'] = ">= 1.9.2"
|
100
|
-
|
101
|
-
# RDoc
|
102
|
-
PROJ.rdoc.exclude << '^ext\/'
|
103
|
-
PROJ.rdoc.opts << '-x' << 'ext'
|
104
|
-
|
105
|
-
# Ruby
|
106
|
-
PROJ.ruby_opts = []
|
107
|
-
PROJ.ruby_opts << '-I' << BUILD_EXT_DIR unless RUBY_PLATFORM == "java"
|
108
|
-
|
109
|
-
# RSpec
|
110
|
-
PROJ.spec.files.exclude /rbx/
|
111
|
-
PROJ.spec.opts << '--color' << '-fs'
|
112
|
-
|
113
|
-
# Dependencies
|
114
|
-
|
115
|
-
#depend_on 'rake', '>=0.8.7'
|
77
|
+
Gem::PackageTask.new(gem_spec) do |pkg|
|
78
|
+
pkg.need_zip = true
|
79
|
+
pkg.need_tar = true
|
80
|
+
pkg.package_dir = 'pkg'
|
81
|
+
end
|
116
82
|
|
117
83
|
TEST_DEPS = [ LIBTEST ]
|
118
84
|
if RUBY_PLATFORM == "java"
|
@@ -144,6 +110,11 @@ task :package => 'gem:package'
|
|
144
110
|
desc "Install the gem locally"
|
145
111
|
task :install => 'gem:install'
|
146
112
|
|
113
|
+
namespace :gem do
|
114
|
+
task :install => :gem do
|
115
|
+
ruby %{ -S gem install pkg/ffi-#{gem_spec.version}.gem }
|
116
|
+
end
|
117
|
+
end
|
147
118
|
|
148
119
|
desc "Clean all built files"
|
149
120
|
task :distclean => :clobber do
|
@@ -158,7 +129,7 @@ end
|
|
158
129
|
|
159
130
|
|
160
131
|
desc "Build the native test lib"
|
161
|
-
|
132
|
+
file "build/libtest.#{LIBEXT}" => FileList['libtest/**/*.[ch]'] do
|
162
133
|
sh %{#{GMAKE} -f libtest/GNUmakefile CPU=#{CPU} OS=#{OS} }
|
163
134
|
end
|
164
135
|
|
@@ -194,3 +165,25 @@ task :default => :specs
|
|
194
165
|
task 'gem:win32' do
|
195
166
|
sh("rake cross native gem RUBY_CC_VERSION='1.8.7:1.9.3'") || raise("win32 build failed!")
|
196
167
|
end
|
168
|
+
|
169
|
+
|
170
|
+
if USE_RAKE_COMPILER
|
171
|
+
Rake::ExtensionTask.new('ffi_c', gem_spec) do |ext|
|
172
|
+
ext.name = 'ffi_c' # indicate the name of the extension.
|
173
|
+
# ext.lib_dir = BUILD_DIR # put binaries into this folder.
|
174
|
+
ext.tmp_dir = BUILD_DIR # temporary folder used during compilation.
|
175
|
+
ext.cross_compile = true # enable cross compilation (requires cross compile toolchain)
|
176
|
+
ext.cross_platform = 'i386-mingw32' # forces the Windows platform instead of the default one
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
begin
|
181
|
+
require 'yard'
|
182
|
+
|
183
|
+
namespace :doc do
|
184
|
+
YARD::Rake::YardocTask.new do |yard|
|
185
|
+
end
|
186
|
+
end
|
187
|
+
rescue LoadError
|
188
|
+
warn "[warn] YARD unavailable"
|
189
|
+
end
|
data/ext/ffi_c/AbstractMemory.c
CHANGED
@@ -431,6 +431,11 @@ memory_put_string(VALUE self, VALUE offset, VALUE str)
|
|
431
431
|
|
432
432
|
checkWrite(ptr);
|
433
433
|
checkBounds(ptr, off, len + 1);
|
434
|
+
|
435
|
+
if (rb_safe_level() >= 1 && OBJ_TAINTED(str)) {
|
436
|
+
rb_raise(rb_eSecurityError, "Writing unsafe string to memory");
|
437
|
+
return Qnil;
|
438
|
+
}
|
434
439
|
|
435
440
|
memcpy(ptr->address + off, RSTRING_PTR(str), len);
|
436
441
|
*((char *) ptr->address + off + len) = '\0';
|
@@ -646,24 +651,25 @@ memory_op_put_strptr(AbstractMemory* ptr, long offset, VALUE value)
|
|
646
651
|
|
647
652
|
static MemoryOp memory_op_strptr = { memory_op_get_strptr, memory_op_put_strptr };
|
648
653
|
|
654
|
+
//static MemoryOp memory_op_pointer = { memory_op_get_pointer, memory_op_put_pointer };
|
649
655
|
|
650
656
|
MemoryOps rbffi_AbstractMemoryOps = {
|
651
|
-
&memory_op_int8,
|
652
|
-
&memory_op_uint8,
|
653
|
-
&memory_op_int16,
|
654
|
-
&memory_op_uint16,
|
655
|
-
&memory_op_int32,
|
656
|
-
&memory_op_uint32,
|
657
|
-
&memory_op_int64,
|
658
|
-
&memory_op_uint64,
|
659
|
-
&memory_op_long,
|
660
|
-
&memory_op_ulong,
|
661
|
-
&memory_op_float32,
|
662
|
-
&memory_op_float64,
|
663
|
-
&memory_op_longdouble,
|
664
|
-
&memory_op_pointer,
|
665
|
-
&memory_op_strptr,
|
666
|
-
&memory_op_bool
|
657
|
+
&memory_op_int8, //.int8
|
658
|
+
&memory_op_uint8, //.uint8
|
659
|
+
&memory_op_int16, //.int16
|
660
|
+
&memory_op_uint16, //.uint16
|
661
|
+
&memory_op_int32, //.int32
|
662
|
+
&memory_op_uint32, //.uint32
|
663
|
+
&memory_op_int64, //.int64
|
664
|
+
&memory_op_uint64, //.uint64
|
665
|
+
&memory_op_long, //.slong
|
666
|
+
&memory_op_ulong, //.uslong
|
667
|
+
&memory_op_float32, //.float32
|
668
|
+
&memory_op_float64, //.float64
|
669
|
+
&memory_op_longdouble, //.longdouble
|
670
|
+
&memory_op_pointer, //.pointer
|
671
|
+
&memory_op_strptr, //.strptr
|
672
|
+
&memory_op_bool //.boolOp
|
667
673
|
};
|
668
674
|
|
669
675
|
void
|
data/ext/ffi_c/AbstractMemory.h
CHANGED
@@ -70,7 +70,7 @@ typedef struct {
|
|
70
70
|
} MemoryOps;
|
71
71
|
|
72
72
|
struct AbstractMemory_ {
|
73
|
-
char* address;
|
73
|
+
char* address; // Use char* instead of void* to ensure adding to it works correctly
|
74
74
|
long size;
|
75
75
|
int flags;
|
76
76
|
int typeSize;
|
data/ext/ffi_c/Buffer.c
CHANGED
@@ -41,7 +41,7 @@ typedef struct Buffer {
|
|
41
41
|
union {
|
42
42
|
VALUE rbParent; /* link to parent buffer */
|
43
43
|
char* storage; /* start of malloc area */
|
44
|
-
long embed[BUFFER_EMBED_MAXLEN / sizeof(long)];
|
44
|
+
long embed[BUFFER_EMBED_MAXLEN / sizeof(long)]; // storage for tiny allocations
|
45
45
|
} data;
|
46
46
|
} Buffer;
|
47
47
|
|
@@ -151,7 +151,7 @@ buffer_initialize_copy(VALUE self, VALUE other)
|
|
151
151
|
dst->memory.size = src->size;
|
152
152
|
dst->memory.typeSize = src->typeSize;
|
153
153
|
|
154
|
-
|
154
|
+
// finally, copy the actual buffer contents
|
155
155
|
memcpy(dst->memory.address, src->address, src->size);
|
156
156
|
|
157
157
|
return self;
|
data/ext/ffi_c/Call.c
CHANGED
@@ -314,10 +314,8 @@ rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo)
|
|
314
314
|
if (unlikely(fnInfo->blocking)) {
|
315
315
|
BlockingCall* bc;
|
316
316
|
|
317
|
-
|
318
|
-
|
319
|
-
* cannot allocate anything passed to the blocking function on the stack
|
320
|
-
*/
|
317
|
+
// due to the way thread switching works on older ruby variants, we
|
318
|
+
// cannot allocate anything passed to the blocking function on the stack
|
321
319
|
ffiValues = ALLOC_N(void *, fnInfo->parameterCount);
|
322
320
|
params = ALLOC_N(FFIStorage, fnInfo->parameterCount);
|
323
321
|
bc = ALLOC_N(BlockingCall, 1);
|
@@ -377,7 +375,10 @@ getPointer(VALUE value, int type)
|
|
377
375
|
return memory != NULL ? memory->address : NULL;
|
378
376
|
|
379
377
|
} else if (type == T_STRING) {
|
380
|
-
|
378
|
+
|
379
|
+
if (rb_safe_level() >= 1 && OBJ_TAINTED(value)) {
|
380
|
+
rb_raise(rb_eSecurityError, "Unsafe string parameter");
|
381
|
+
}
|
381
382
|
return StringValuePtr(value);
|
382
383
|
|
383
384
|
} else if (type == T_NIL) {
|
@@ -431,13 +432,14 @@ callback_param(VALUE proc, VALUE cbInfo)
|
|
431
432
|
return NULL ;
|
432
433
|
}
|
433
434
|
|
434
|
-
|
435
|
+
// Handle Function pointers here
|
435
436
|
if (rb_obj_is_kind_of(proc, rbffi_FunctionClass)) {
|
436
437
|
AbstractMemory* ptr;
|
437
438
|
Data_Get_Struct(proc, AbstractMemory, ptr);
|
438
439
|
return ptr->address;
|
439
440
|
}
|
440
441
|
|
442
|
+
//callback = rbffi_NativeCallback_ForProc(proc, cbInfo);
|
441
443
|
callback = rbffi_Function_ForProc(cbInfo, proc);
|
442
444
|
RB_GC_GUARD(callback);
|
443
445
|
|
data/ext/ffi_c/ClosurePool.c
CHANGED
@@ -206,7 +206,7 @@ rbffi_Closure_Free(Closure* closure)
|
|
206
206
|
if (closure != NULL) {
|
207
207
|
ClosurePool* pool = closure->pool;
|
208
208
|
long refcnt;
|
209
|
-
|
209
|
+
// Just push it on the front of the free list
|
210
210
|
closure->next = pool->list;
|
211
211
|
pool->list = closure;
|
212
212
|
refcnt = --(pool->refcnt);
|
data/ext/ffi_c/DynamicLibrary.c
CHANGED
@@ -155,7 +155,7 @@ library_dlerror(VALUE self)
|
|
155
155
|
static void
|
156
156
|
library_free(Library* library)
|
157
157
|
{
|
158
|
-
|
158
|
+
// dlclose() on MacOS tends to segfault - avoid it
|
159
159
|
#ifndef __APPLE__
|
160
160
|
if (library->handle != NULL) {
|
161
161
|
dl_close(library->handle);
|
@@ -270,7 +270,7 @@ rbffi_DynamicLibrary_Init(VALUE moduleFFI)
|
|
270
270
|
* Document-const: FFI::NativeLibrary
|
271
271
|
* Backward compatibility for FFI::DynamicLibrary
|
272
272
|
*/
|
273
|
-
rb_define_const(moduleFFI, "NativeLibrary", LibraryClass);
|
273
|
+
rb_define_const(moduleFFI, "NativeLibrary", LibraryClass); // backwards compat library
|
274
274
|
rb_define_alloc_func(LibraryClass, library_allocate);
|
275
275
|
rb_define_singleton_method(LibraryClass, "open", library_open, 2);
|
276
276
|
rb_define_singleton_method(LibraryClass, "last_error", library_dlerror, 0);
|
data/ext/ffi_c/Function.c
CHANGED
@@ -199,11 +199,11 @@ function_initialize(int argc, VALUE* argv, VALUE self)
|
|
199
199
|
|
200
200
|
nargs = rb_scan_args(argc, argv, "22", &rbReturnType, &rbParamTypes, &rbProc, &rbOptions);
|
201
201
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
202
|
+
//
|
203
|
+
// Callback with block,
|
204
|
+
// e.g. Function.new(:int, [ :int ]) { |i| blah }
|
205
|
+
// or Function.new(:int, [ :int ], { :convention => :stdcall }) { |i| blah }
|
206
|
+
//
|
207
207
|
if (rb_block_given_p()) {
|
208
208
|
if (nargs > 3) {
|
209
209
|
rb_raise(rb_eArgError, "cannot create function with both proc/address and block");
|
@@ -211,12 +211,11 @@ function_initialize(int argc, VALUE* argv, VALUE self)
|
|
211
211
|
rbOptions = rbProc;
|
212
212
|
rbProc = rb_block_proc();
|
213
213
|
} else {
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
*/
|
214
|
+
// Callback with proc, or Function with address
|
215
|
+
// e.g. Function.new(:int, [ :int ], Proc.new { |i| })
|
216
|
+
// Function.new(:int, [ :int ], Proc.new { |i| }, { :convention => :stdcall })
|
217
|
+
// Function.new(:int, [ :int ], addr)
|
218
|
+
// Function.new(:int, [ :int ], addr, { :convention => :stdcall })
|
220
219
|
}
|
221
220
|
|
222
221
|
infoArgv[0] = rbReturnType;
|
@@ -382,9 +381,9 @@ function_attach(VALUE self, VALUE module, VALUE name)
|
|
382
381
|
fn->methodHandle = rbffi_MethodHandle_Alloc(fn->info, fn->base.memory.address);
|
383
382
|
}
|
384
383
|
|
385
|
-
|
386
|
-
|
387
|
-
|
384
|
+
//
|
385
|
+
// Stash the Function in a module variable so it does not get garbage collected
|
386
|
+
//
|
388
387
|
snprintf(var, sizeof(var), "@@%s", StringValueCStr(name));
|
389
388
|
rb_cv_set(module, var, self);
|
390
389
|
|
@@ -471,7 +470,7 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
|
|
471
470
|
pthread_mutex_init(&cb.async_mutex, NULL);
|
472
471
|
pthread_cond_init(&cb.async_cond, NULL);
|
473
472
|
|
474
|
-
|
473
|
+
// Now signal the async callback thread
|
475
474
|
pthread_mutex_lock(&async_cb_mutex);
|
476
475
|
empty = async_cb_list == NULL;
|
477
476
|
cb.next = async_cb_list;
|
@@ -479,7 +478,7 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
|
|
479
478
|
pthread_mutex_unlock(&async_cb_mutex);
|
480
479
|
|
481
480
|
#if !defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
482
|
-
|
481
|
+
// Only signal if the list was empty
|
483
482
|
if (empty) {
|
484
483
|
char c;
|
485
484
|
write(async_cb_pipe[1], &c, 1);
|
@@ -488,7 +487,7 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
|
|
488
487
|
pthread_cond_signal(&async_cb_cond);
|
489
488
|
#endif
|
490
489
|
|
491
|
-
|
490
|
+
// Wait for the thread executing the ruby callback to signal it is done
|
492
491
|
pthread_mutex_lock(&cb.async_mutex);
|
493
492
|
while (!cb.done) {
|
494
493
|
pthread_cond_wait(&cb.async_cond, &cb.async_mutex);
|
@@ -503,7 +502,7 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
|
|
503
502
|
|
504
503
|
cb.async_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
505
504
|
|
506
|
-
|
505
|
+
// Now signal the async callback thread
|
507
506
|
EnterCriticalSection(&async_cb_lock);
|
508
507
|
empty = async_cb_list == NULL;
|
509
508
|
cb.next = async_cb_list;
|
@@ -511,7 +510,7 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
|
|
511
510
|
LeaveCriticalSection(&async_cb_lock);
|
512
511
|
|
513
512
|
#if !defined(HAVE_RB_THREAD_BLOCKING_REGION)
|
514
|
-
|
513
|
+
// Only signal if the list was empty
|
515
514
|
if (empty) {
|
516
515
|
char c;
|
517
516
|
write(async_cb_pipe[1], &c, 1);
|
@@ -520,7 +519,7 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
|
|
520
519
|
SetEvent(async_cb_cond);
|
521
520
|
#endif
|
522
521
|
|
523
|
-
|
522
|
+
// Wait for the thread executing the ruby callback to signal it is done
|
524
523
|
WaitForSingleObject(cb.async_event, INFINITE);
|
525
524
|
CloseHandle(cb.async_event);
|
526
525
|
#endif
|
@@ -546,7 +545,7 @@ async_cb_event(void* unused)
|
|
546
545
|
while (!w.stop) {
|
547
546
|
rb_thread_blocking_region(async_cb_wait, &w, async_cb_stop, &w);
|
548
547
|
if (w.cb != NULL) {
|
549
|
-
|
548
|
+
// Start up a new ruby thread to run the ruby callback
|
550
549
|
rb_thread_create(async_cb_call, w.cb);
|
551
550
|
}
|
552
551
|
}
|
@@ -575,7 +574,7 @@ async_cb_event(void* unused)
|
|
575
574
|
|
576
575
|
while (cb != NULL) {
|
577
576
|
struct gvl_callback* next = cb->next;
|
578
|
-
|
577
|
+
// Start up a new ruby thread to run the ruby callback
|
579
578
|
rb_thread_create(async_cb_call, cb);
|
580
579
|
cb = next;
|
581
580
|
}
|
@@ -607,7 +606,7 @@ async_cb_event(void* unused)
|
|
607
606
|
|
608
607
|
while (cb != NULL) {
|
609
608
|
struct gvl_callback* next = cb->next;
|
610
|
-
|
609
|
+
// Start up a new ruby thread to run the ruby callback
|
611
610
|
rb_thread_create(async_cb_call, cb);
|
612
611
|
cb = next;
|
613
612
|
}
|
@@ -697,7 +696,7 @@ async_cb_call(void *data)
|
|
697
696
|
|
698
697
|
callback_with_gvl(cb);
|
699
698
|
|
700
|
-
|
699
|
+
// Signal the original native thread that the ruby code has completed
|
701
700
|
#ifdef _WIN32
|
702
701
|
SetEvent(cb->async_event);
|
703
702
|
#else
|
@@ -800,7 +799,7 @@ callback_with_gvl(void* data)
|
|
800
799
|
break;
|
801
800
|
}
|
802
801
|
|
803
|
-
|
802
|
+
// Convert the native value into a custom ruby value
|
804
803
|
if (unlikely(cbInfo->parameterTypes[i]->nativeType == NATIVE_MAPPED)) {
|
805
804
|
VALUE values[] = { param, Qnil };
|
806
805
|
param = rb_funcall2(((MappedType *) cbInfo->parameterTypes[i])->rbConverter, id_from_native, 2, values);
|
@@ -854,7 +853,7 @@ callback_with_gvl(void* data)
|
|
854
853
|
if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_PointerClass)) {
|
855
854
|
*((void **) retval) = ((AbstractMemory *) DATA_PTR(rbReturnValue))->address;
|
856
855
|
} else {
|
857
|
-
|
856
|
+
// Default to returning NULL if not a value pointer object. handles nil case as well
|
858
857
|
*((void **) retval) = NULL;
|
859
858
|
}
|
860
859
|
break;
|