diff --git a/ext/ffi_c/Call.c b/ext/ffi_c/Call.c index d0a712250..3a7c6903e 100644 --- a/ext/ffi_c/Call.c +++ b/ext/ffi_c/Call.c @@ -43,10 +43,8 @@ #endif #include #include -#if defined(HAVE_RUBY_THREAD_H) #include -#endif -#if defined(HAVE_NATIVETHREAD) && (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) && !defined(_WIN32) +#if defined(HAVE_NATIVETHREAD) && !defined(_WIN32) # include # include #endif @@ -359,7 +357,7 @@ call_blocking_function(void* data) VALUE rbffi_do_blocking_call(void *data) { - rbffi_thread_blocking_region(call_blocking_function, data, (void *) -1, NULL); + rb_thread_call_without_gvl(call_blocking_function, data, (void *) -1, NULL); return Qnil; } @@ -386,22 +384,11 @@ rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo) if (unlikely(fnInfo->blocking)) { rbffi_blocking_call_t* bc; - /* - * due to the way thread switching works on older ruby variants, we - * cannot allocate anything passed to the blocking function on the stack - */ -#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) + /* allocate information passed to the blocking function on the stack */ ffiValues = ALLOCA_N(void *, fnInfo->parameterCount); params = ALLOCA_N(FFIStorage, fnInfo->parameterCount); bc = ALLOCA_N(rbffi_blocking_call_t, 1); bc->retval = retval; -#else - ffiValues = ALLOC_N(void *, fnInfo->parameterCount); - params = ALLOC_N(FFIStorage, fnInfo->parameterCount); - bc = ALLOC_N(rbffi_blocking_call_t, 1); - bc->retval = xmalloc(MAX(fnInfo->ffi_cif.rtype->size, FFI_SIZEOF_ARG)); - bc->stkretval = retval; -#endif bc->cif = fnInfo->ffi_cif; bc->function = function; bc->ffiValues = ffiValues; @@ -416,14 +403,6 @@ rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo) rb_rescue2(rbffi_do_blocking_call, (VALUE) bc, rbffi_save_frame_exception, (VALUE) &frame, rb_eException, (VALUE) 0); rbffi_frame_pop(&frame); -#if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) - memcpy(bc->stkretval, bc->retval, MAX(bc->cif.rtype->size, FFI_SIZEOF_ARG)); - xfree(bc->params); - xfree(bc->ffiValues); - xfree(bc->retval); - xfree(bc); -#endif - } else { ffiValues = ALLOCA_N(void *, fnInfo->parameterCount); diff --git a/ext/ffi_c/Call.h b/ext/ffi_c/Call.h index dc3f9825e..d9b9eda22 100644 --- a/ext/ffi_c/Call.h +++ b/ext/ffi_c/Call.h @@ -94,9 +94,6 @@ typedef struct rbffi_blocking_call { void **ffiValues; void* retval; void* params; -#if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) - void* stkretval; -#endif } rbffi_blocking_call_t; VALUE rbffi_do_blocking_call(void* data); diff --git a/ext/ffi_c/Function.c b/ext/ffi_c/Function.c index 7bbd30461..b44a03b7f 100644 --- a/ext/ffi_c/Function.c +++ b/ext/ffi_c/Function.c @@ -47,9 +47,7 @@ # endif #endif #include -#if defined(HAVE_RUBY_THREAD_H) #include -#endif #include #if defined(HAVE_NATIVETHREAD) && !defined(_WIN32) @@ -101,10 +99,6 @@ static VALUE async_cb_event(void *); static VALUE async_cb_call(void *); #endif -#ifdef HAVE_RB_THREAD_CALL_WITH_GVL -extern void *rb_thread_call_with_gvl(void *(*func)(void *), void *data1); -#endif - #ifdef HAVE_RUBY_THREAD_HAS_GVL_P extern int ruby_thread_has_gvl_p(void); #define rbffi_thread_has_gvl_p(frame) ruby_thread_has_gvl_p() @@ -157,15 +151,9 @@ static struct gvl_callback* async_cb_list = NULL; # ifndef _WIN32 static pthread_mutex_t async_cb_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t async_cb_cond = PTHREAD_COND_INITIALIZER; -# if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) - static int async_cb_pipe[2]; -# endif # else static HANDLE async_cb_cond; static CRITICAL_SECTION async_cb_lock; -# if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) - static int async_cb_pipe[2]; -# endif # endif #endif @@ -344,16 +332,8 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc) #if defined(DEFER_ASYNC_CALLBACK) if (async_cb_thread == Qnil) { -#if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) && defined(_WIN32) - _pipe(async_cb_pipe, 1024, O_BINARY); -#elif !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) - pipe(async_cb_pipe); - fcntl(async_cb_pipe[0], F_SETFL, fcntl(async_cb_pipe[0], F_GETFL) | O_NONBLOCK); - fcntl(async_cb_pipe[1], F_SETFL, fcntl(async_cb_pipe[1], F_GETFL) | O_NONBLOCK); -#endif async_cb_thread = rb_thread_create(async_cb_event, NULL); } - #endif fn->closure = rbffi_Closure_Alloc(fn->info->closurePool); @@ -515,17 +495,8 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data) cb.next = async_cb_list; async_cb_list = &cb; -#if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) - pthread_mutex_unlock(&async_cb_mutex); - /* Only signal if the list was empty */ - if (empty) { - char c; - write(async_cb_pipe[1], &c, 1); - } -#else pthread_cond_signal(&async_cb_cond); pthread_mutex_unlock(&async_cb_mutex); -#endif /* Wait for the thread executing the ruby callback to signal it is done */ pthread_mutex_lock(&cb.async_mutex); @@ -549,15 +520,7 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data) async_cb_list = &cb; LeaveCriticalSection(&async_cb_lock); -#if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) - /* Only signal if the list was empty */ - if (empty) { - char c; - write(async_cb_pipe[1], &c, 1); - } -#else SetEvent(async_cb_cond); -#endif /* Wait for the thread executing the ruby callback to signal it is done */ WaitForSingleObject(cb.async_event, INFINITE); @@ -575,7 +538,6 @@ struct async_wait { static void * async_cb_wait(void *); static void async_cb_stop(void *); -#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) static VALUE async_cb_event(void* unused) { @@ -583,11 +545,7 @@ async_cb_event(void* unused) w.stop = false; while (!w.stop) { -#if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) rb_thread_call_without_gvl(async_cb_wait, &w, async_cb_stop, &w); -#else - rb_thread_blocking_region(async_cb_wait, &w, async_cb_stop, &w); -#endif if (w.cb != NULL) { /* Start up a new ruby thread to run the ruby callback */ rb_thread_create(async_cb_call, w.cb); @@ -597,69 +555,6 @@ async_cb_event(void* unused) return Qnil; } -#elif defined(_WIN32) -static VALUE -async_cb_event(void* unused) -{ - while (true) { - struct gvl_callback* cb; - char buf[64]; - fd_set rfds; - - FD_ZERO(&rfds); - FD_SET(async_cb_pipe[0], &rfds); - rb_thread_select(async_cb_pipe[0] + 1, &rfds, NULL, NULL, NULL); - read(async_cb_pipe[0], buf, sizeof(buf)); - - EnterCriticalSection(&async_cb_lock); - cb = async_cb_list; - async_cb_list = NULL; - LeaveCriticalSection(&async_cb_lock); - - while (cb != NULL) { - struct gvl_callback* next = cb->next; - /* Start up a new ruby thread to run the ruby callback */ - rb_thread_create(async_cb_call, cb); - cb = next; - } - } - - return Qnil; -} -#else -static VALUE -async_cb_event(void* unused) -{ - while (true) { - struct gvl_callback* cb; - char buf[64]; - - if (read(async_cb_pipe[0], buf, sizeof(buf)) < 0) { - rb_thread_wait_fd(async_cb_pipe[0]); - while (read(async_cb_pipe[0], buf, sizeof (buf)) < 0) { - if (rb_io_wait_readable(async_cb_pipe[0]) != Qtrue) { - return Qfalse; - } - } - } - - pthread_mutex_lock(&async_cb_mutex); - cb = async_cb_list; - async_cb_list = NULL; - pthread_mutex_unlock(&async_cb_mutex); - - while (cb != NULL) { - struct gvl_callback* next = cb->next; - /* Start up a new ruby thread to run the ruby callback */ - rb_thread_create(async_cb_call, cb); - cb = next; - } - } - - return Qnil; -} -#endif - #ifdef _WIN32 static void * async_cb_wait(void *data) diff --git a/ext/ffi_c/Thread.c b/ext/ffi_c/Thread.c index f59c2b8dd..10cdc5b82 100644 --- a/ext/ffi_c/Thread.c +++ b/ext/ffi_c/Thread.c @@ -99,224 +99,6 @@ rbffi_frame_pop(rbffi_frame_t* frame) #endif } -#if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)) - -#if !defined(_WIN32) - -struct BlockingThread { - pthread_t tid; - VALUE (*fn)(void *); - void *data; - void (*ubf)(void *); - void *data2; - VALUE retval; - int wrfd; - int rdfd; -}; - -static void* -rbffi_blocking_thread(void* args) -{ - struct BlockingThread* thr = (struct BlockingThread *) args; - char c = 1; - VALUE retval; - - retval = (*thr->fn)(thr->data); - - pthread_testcancel(); - - thr->retval = retval; - - write(thr->wrfd, &c, sizeof(c)); - - return NULL; -} - -static VALUE -wait_for_thread(void *data) -{ - struct BlockingThread* thr = (struct BlockingThread *) data; - char c; - - if (read(thr->rdfd, &c, 1) < 1) { - rb_thread_wait_fd(thr->rdfd); - while (read(thr->rdfd, &c, 1) < 1 && rb_io_wait_readable(thr->rdfd) == Qtrue) { - ; - } - } - - return Qnil; -} - -static VALUE -cleanup_blocking_thread(void *data, VALUE exc) -{ - struct BlockingThread* thr = (struct BlockingThread *) data; - - if (thr->ubf != (void (*)(void *)) -1) { - (*thr->ubf)(thr->data2); - } else { - pthread_kill(thr->tid, SIGVTALRM); - } - - return exc; -} - -VALUE -rbffi_thread_blocking_region(VALUE (*func)(void *), void *data1, void (*ubf)(void *), void *data2) -{ - struct BlockingThread* thr; - int fd[2]; - VALUE exc; - - if (pipe(fd) < 0) { - rb_raise(rb_eSystemCallError, "pipe(2) failed"); - return Qnil; - } - fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | O_NONBLOCK); - - thr = ALLOC_N(struct BlockingThread, 1); - thr->rdfd = fd[0]; - thr->wrfd = fd[1]; - thr->fn = func; - thr->data = data1; - thr->ubf = ubf; - thr->data2 = data2; - thr->retval = Qnil; - - if (pthread_create(&thr->tid, NULL, rbffi_blocking_thread, thr) != 0) { - close(fd[0]); - close(fd[1]); - xfree(thr); - rb_raise(rb_eSystemCallError, "pipe(2) failed"); - return Qnil; - } - - exc = rb_rescue2(wait_for_thread, (VALUE) thr, cleanup_blocking_thread, (VALUE) thr, - rb_eException); - - pthread_join(thr->tid, NULL); - close(fd[1]); - close(fd[0]); - xfree(thr); - - if (exc != Qnil) { - rb_exc_raise(exc); - } - - return thr->retval; -} - -#else -/* win32 implementation */ - -struct BlockingThread { - HANDLE tid; - VALUE (*fn)(void *); - void *data; - void (*ubf)(void *); - void *data2; - VALUE retval; - int wrfd; - int rdfd; -}; - -static DWORD __stdcall -rbffi_blocking_thread(LPVOID args) -{ - struct BlockingThread* thr = (struct BlockingThread *) args; - char c = 1; - VALUE retval; - - retval = (*thr->fn)(thr->data); - thr->retval = retval; - - write(thr->wrfd, &c, sizeof(c)); - - return 0; -} - -static VALUE -wait_for_thread(void *data) -{ - struct BlockingThread* thr = (struct BlockingThread *) data; - char c, res; - fd_set rfds; - - FD_ZERO(&rfds); - FD_SET(thr->rdfd, &rfds); - rb_thread_select(thr->rdfd + 1, &rfds, NULL, NULL, NULL); - read(thr->rdfd, &c, 1); - return Qnil; -} - -static VALUE -cleanup_blocking_thread(void *data, VALUE exc) -{ - struct BlockingThread* thr = (struct BlockingThread *) data; - - if (thr->ubf != (void (*)(void *)) -1) { - (*thr->ubf)(thr->data2); - } else { - TerminateThread(thr->tid, 0); - } - - return exc; -} - -VALUE -rbffi_thread_blocking_region(VALUE (*func)(void *), void *data1, void (*ubf)(void *), void *data2) -{ - struct BlockingThread* thr; - int fd[2]; - VALUE exc; - DWORD state; - DWORD res; - - if (_pipe(fd, 1024, O_BINARY) == -1) { - rb_raise(rb_eSystemCallError, "_pipe() failed"); - return Qnil; - } - - thr = ALLOC_N(struct BlockingThread, 1); - thr->rdfd = fd[0]; - thr->wrfd = fd[1]; - thr->fn = func; - thr->data = data1; - thr->ubf = ubf; - thr->data2 = data2; - thr->retval = Qnil; - - thr->tid = CreateThread(NULL, 0, rbffi_blocking_thread, thr, 0, NULL); - if (!thr->tid) { - close(fd[0]); - close(fd[1]); - xfree(thr); - rb_raise(rb_eSystemCallError, "CreateThread() failed"); - return Qnil; - } - - exc = rb_rescue2(wait_for_thread, (VALUE) thr, cleanup_blocking_thread, (VALUE) thr, - rb_eException); - - /* The thread should be finished, already. */ - WaitForSingleObject(thr->tid, INFINITE); - CloseHandle(thr->tid); - close(fd[1]); - close(fd[0]); - xfree(thr); - - if (exc != Qnil) { - rb_exc_raise(exc); - } - - return thr->retval; -} - -#endif /* !_WIN32 */ - -#endif /* HAVE_RB_THREAD_BLOCKING_REGION */ - #ifndef _WIN32 static struct thread_data* thread_data_init(void); diff --git a/ext/ffi_c/Thread.h b/ext/ffi_c/Thread.h index b660b36e6..485f2bbc9 100644 --- a/ext/ffi_c/Thread.h +++ b/ext/ffi_c/Thread.h @@ -76,19 +76,6 @@ rbffi_frame_t* rbffi_frame_current(void); void rbffi_frame_push(rbffi_frame_t* frame); void rbffi_frame_pop(rbffi_frame_t* frame); -#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL -# define rbffi_thread_blocking_region rb_thread_call_without_gvl - -#elif defined(HAVE_RB_THREAD_BLOCKING_REGION) -# define rbffi_thread_blocking_region rb_thread_blocking_region - -#else - -VALUE rbffi_thread_blocking_region(VALUE (*func)(void *), void *data1, void (*ubf)(void *), void *data2); - -#endif - - #ifdef __cplusplus } #endif diff --git a/ext/ffi_c/Variadic.c b/ext/ffi_c/Variadic.c index 877ffaba5..aafe07284 100644 --- a/ext/ffi_c/Variadic.c +++ b/ext/ffi_c/Variadic.c @@ -191,7 +191,7 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues) for (i = 0; i < paramCount; ++i) { VALUE rbType = rb_ary_entry(parameterTypes, i); - + if (!rb_obj_is_kind_of(rbType, rbffi_TypeClass)) { rb_raise(rb_eTypeError, "wrong type. Expected (FFI::Type)"); } @@ -210,7 +210,7 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues) rbType = rb_const_get(rbffi_TypeClass, rb_intern("UINT32")); Data_Get_Struct(rbType, Type, paramTypes[i]); break; - + case NATIVE_FLOAT32: rbType = rb_const_get(rbffi_TypeClass, rb_intern("DOUBLE")); Data_Get_Struct(rbType, Type, paramTypes[i]); @@ -219,8 +219,8 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues) default: break; } - - + + ffiParamTypes[i] = paramTypes[i]->ffiType; if (ffiParamTypes[i] == NULL) { rb_raise(rb_eArgError, "Invalid parameter type #%x", paramTypes[i]->nativeType); @@ -254,13 +254,9 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues) rbffi_SetupCallParams(paramCount, argv, -1, paramTypes, params, ffiValues, NULL, 0, invoker->rbEnums); - + rbffi_frame_push(&frame); -#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL - /* In Call.c, blocking: true is supported on older ruby variants - * without rb_thread_call_without_gvl by allocating on the heap instead - * of the stack. Since this functionality is being added later, - * we’re skipping support for old rubies here. */ + if(unlikely(invoker->blocking)) { rbffi_blocking_call_t* bc; bc = ALLOCA_N(rbffi_blocking_call_t, 1); @@ -275,13 +271,11 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues) } else { ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues); } -#else - ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues); -#endif + rbffi_frame_pop(&frame); - + rbffi_save_errno(); - + if (RTEST(frame.exc) && frame.exc != Qnil) { rb_exc_raise(frame.exc); } diff --git a/ext/ffi_c/extconf.rb b/ext/ffi_c/extconf.rb index 6818897e8..f1bb035db 100644 --- a/ext/ffi_c/extconf.rb +++ b/ext/ffi_c/extconf.rb @@ -37,10 +37,7 @@ def system_libffi_usable? end have_header('shlwapi.h') - have_header('ruby/thread.h') # for compat with ruby < 2.0 - have_func('rb_thread_blocking_region') - have_func('rb_thread_call_with_gvl') - have_func('rb_thread_call_without_gvl') + have_func('rb_thread_call_without_gvl') || abort("Ruby C-API function `rb_thread_call_without_gvl` is missing") have_func('ruby_native_thread_p') if RbConfig::CONFIG['host_os'].downcase !~ /darwin/ || RUBY_VERSION >= "2.3.0" # On OSX ruby_thread_has_gvl_p is detected but fails at runtime for ruby < 2.3.0