summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Kanis <lars@greiz-reinsdorf.de>2021-03-05 20:55:46 +0100
committerGitHub <noreply@github.com>2021-03-05 20:55:46 +0100
commit05537c72095ced1915c0458de39a4171915d04f8 (patch)
treeda1ac89cd3f1527f4ed4024cab8a7f3e5232f01e
parentea13267c876f28e475c04cb38d645d9fb563b557 (diff)
parentdf39cc6b5ece580e8e8f5d0964f32b23e8d80796 (diff)
downloadffi-05537c72095ced1915c0458de39a4171915d04f8.tar.gz
Merge pull request #888 from larskanis/async-callback-with-fork
Fix async callbacks in conjunction with fork()
-rw-r--r--CHANGELOG.md1
-rw-r--r--ext/ffi_c/Function.c18
-rw-r--r--spec/ffi/fork_spec.rb37
3 files changed, 51 insertions, 5 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 847f946..237a20b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
Fixed:
* Fix MSVC build
+* Fix async callbacks in conjunction with fork(). #884
Added:
* Allow to pass callbacks in varargs. #885
diff --git a/ext/ffi_c/Function.c b/ext/ffi_c/Function.c
index 11d0a6d..1a57591 100644
--- a/ext/ffi_c/Function.c
+++ b/ext/ffi_c/Function.c
@@ -282,6 +282,17 @@ rbffi_Function_ForProc(VALUE rbFunctionInfo, VALUE proc)
return callback;
}
+#if !defined(_WIN32) && defined(DEFER_ASYNC_CALLBACK)
+static void
+after_fork_callback(void)
+{
+ /* Ensure that a new dispatcher thread is started in a forked process */
+ async_cb_thread = Qnil;
+ pthread_mutex_init(&async_cb_mutex, NULL);
+ pthread_cond_init(&async_cb_cond, NULL);
+}
+#endif
+
static VALUE
function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
{
@@ -309,6 +320,13 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
#if defined(DEFER_ASYNC_CALLBACK)
if (async_cb_thread == Qnil) {
+
+#if !defined(_WIN32)
+ if( pthread_atfork(NULL, NULL, after_fork_callback) ){
+ rb_warn("FFI: unable to register fork callback");
+ }
+#endif
+
async_cb_thread = rb_thread_create(async_cb_event, NULL);
/* Name thread, for better debugging */
rb_funcall(async_cb_thread, rb_intern("name="), 1, rb_str_new2("FFI Callback Dispatcher"));
diff --git a/spec/ffi/fork_spec.rb b/spec/ffi/fork_spec.rb
index 635d939..2c663ee 100644
--- a/spec/ffi/fork_spec.rb
+++ b/spec/ffi/fork_spec.rb
@@ -8,21 +8,26 @@ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
if Process.respond_to?(:fork)
describe "Callback in conjunction with fork()" do
- module LibTest
+ libtest = Module.new do
extend FFI::Library
ffi_lib TestLibrary::PATH
callback :cbVrL, [ ], :long
attach_function :testCallbackVrL, :testClosureVrL, [ :cbVrL ], :long
+ AsyncIntCallback = callback [ :int ], :void
+ attach_function :testAsyncCallback, [ AsyncIntCallback, :int ], :void, blocking: true
end
it "works with forked process and GC" do
- expect(LibTest.testCallbackVrL { 12345 }).to eq(12345)
+ expect(libtest.testCallbackVrL { 12345 }).to eq(12345)
fork do
- expect(LibTest.testCallbackVrL { 12345 }).to eq(12345)
+ expect(libtest.testCallbackVrL { 12345 }).to eq(12345)
+ Process.exit 42
end
- expect(LibTest.testCallbackVrL { 12345 }).to eq(12345)
+ expect(libtest.testCallbackVrL { 12345 }).to eq(12345)
GC.start
+
+ expect(Process.wait2[1].exitstatus).to eq(42)
end
it "works with forked process and free()" do
@@ -30,10 +35,32 @@ if Process.respond_to?(:fork)
fork do
cbf.free
+ Process.exit 43
end
- expect(LibTest.testCallbackVrL(cbf)).to eq(234)
+ expect(libtest.testCallbackVrL(cbf)).to eq(234)
cbf.free
+
+ expect(Process.wait2[1].exitstatus).to eq(43)
+ end
+
+ def run_async_callback(libtest)
+ recv = nil
+ libtest.testAsyncCallback(proc { |r| recv = r }, 23)
+ expect(recv).to eq(23)
+ end
+
+ it "async thread dispatch works after forking" do
+ run_async_callback(libtest)
+
+ fork do
+ run_async_callback(libtest)
+ Process.exit 44
+ end
+
+ run_async_callback(libtest)
+
+ expect(Process.wait2[1].exitstatus).to eq(44)
end
end
end