Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Add rbffi_thread_blocking_region and DEFER_ASYNC_CALLBACK for Ruby 1.…

…8 on win32

This mostly done by replacing posix with win32 functions,
but it's working with rb_thread_select() and blocking _pipes,
since rb_io_wait_readable() doesn't work with nonblocking pipes.
  • Loading branch information...
commit 23aedab7ca947845bec5f379c0c66bb7acc4ad86 1 parent 149f544
Lars Kanis authored

Showing 2 changed files with 160 additions and 10 deletions. Show diff stats Hide diff stats

  1. +53 10 ext/ffi_c/Function.c
  2. +107 0 ext/ffi_c/Thread.c
63 ext/ffi_c/Function.c
@@ -69,9 +69,7 @@ static void callback_invoke(ffi_cif* cif, void* retval, void** parameters, void*
69 69 static bool callback_prep(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize);
70 70 static void* callback_with_gvl(void* data);
71 71
72   -#if !defined(_WIN32) || defined(HAVE_RB_THREAD_BLOCKING_REGION)
73   -# define DEFER_ASYNC_CALLBACK 1
74   -#endif
  72 +#define DEFER_ASYNC_CALLBACK 1
75 73
76 74
77 75 #if defined(DEFER_ASYNC_CALLBACK)
@@ -117,8 +115,11 @@ static struct gvl_callback* async_cb_list = NULL;
117 115 static int async_cb_pipe[2];
118 116 # endif
119 117 # else
120   -static HANDLE async_cb_cond;
121   -static CRITICAL_SECTION async_cb_lock;
  118 + static HANDLE async_cb_cond;
  119 + static CRITICAL_SECTION async_cb_lock;
  120 +# if !defined(HAVE_RB_THREAD_BLOCKING_REGION)
  121 + static int async_cb_pipe[2];
  122 +# endif
122 123 # endif
123 124 #endif
124 125
@@ -277,7 +278,9 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
277 278
278 279 #if defined(DEFER_ASYNC_CALLBACK)
279 280 if (async_cb_thread == Qnil) {
280   -#if !defined(HAVE_RB_THREAD_BLOCKING_REGION)
  281 +#if !defined(HAVE_RB_THREAD_BLOCKING_REGION) && defined(_WIN32)
  282 + _pipe(async_cb_pipe, 1024, O_BINARY);
  283 +#elif !defined(HAVE_RB_THREAD_BLOCKING_REGION)
281 284 pipe(async_cb_pipe);
282 285 fcntl(async_cb_pipe[0], F_SETFL, fcntl(async_cb_pipe[0], F_GETFL) | O_NONBLOCK);
283 286 fcntl(async_cb_pipe[1], F_SETFL, fcntl(async_cb_pipe[1], F_GETFL) | O_NONBLOCK);
@@ -441,16 +444,27 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
441 444
442 445 #elif defined(DEFER_ASYNC_CALLBACK) && defined(_WIN32)
443 446 } else {
  447 + bool empty = false;
  448 +
444 449 cb.async_event = CreateEvent(NULL, FALSE, FALSE, NULL);
445   -
  450 +
446 451 // Now signal the async callback thread
447 452 EnterCriticalSection(&async_cb_lock);
  453 + empty = async_cb_list == NULL;
448 454 cb.next = async_cb_list;
449 455 async_cb_list = &cb;
450 456 LeaveCriticalSection(&async_cb_lock);
451 457
  458 +#if !defined(HAVE_RB_THREAD_BLOCKING_REGION)
  459 + // Only signal if the list was empty
  460 + if (empty) {
  461 + char c;
  462 + write(async_cb_pipe[1], &c, 1);
  463 + }
  464 +#else
452 465 SetEvent(async_cb_cond);
453   -
  466 +#endif
  467 +
454 468 // Wait for the thread executing the ruby callback to signal it is done
455 469 WaitForSingleObject(cb.async_event, INFINITE);
456 470 CloseHandle(cb.async_event);
@@ -481,10 +495,39 @@ async_cb_event(void* unused)
481 495 rb_thread_create(async_cb_call, w.cb);
482 496 }
483 497 }
484   -
  498 +
485 499 return Qnil;
486 500 }
487 501
  502 +#elif defined(_WIN32)
  503 +static VALUE
  504 +async_cb_event(void* unused)
  505 +{
  506 + while (true) {
  507 + struct gvl_callback* cb;
  508 + char buf[64];
  509 + fd_set rfds;
  510 +
  511 + FD_ZERO(&rfds);
  512 + FD_SET(async_cb_pipe[0], &rfds);
  513 + rb_thread_select(async_cb_pipe[0] + 1, &rfds, NULL, NULL, NULL);
  514 + read(async_cb_pipe[0], buf, sizeof(buf));
  515 +
  516 + EnterCriticalSection(&async_cb_lock);
  517 + cb = async_cb_list;
  518 + async_cb_list = NULL;
  519 + LeaveCriticalSection(&async_cb_lock);
  520 +
  521 + while (cb != NULL) {
  522 + struct gvl_callback* next = cb->next;
  523 + // Start up a new ruby thread to run the ruby callback
  524 + rb_thread_create(async_cb_call, cb);
  525 + cb = next;
  526 + }
  527 + }
  528 +
  529 + return Qnil;
  530 +}
488 531 #else
489 532 static VALUE
490 533 async_cb_event(void* unused)
@@ -842,7 +885,7 @@ rbffi_Function_Init(VALUE moduleFFI)
842 885 id_cb_ref = rb_intern("@__ffi_callback__");
843 886 id_to_native = rb_intern("to_native");
844 887 id_from_native = rb_intern("from_native");
845   -#if defined(_WIN32) && defined(HAVE_RB_THREAD_BLOCKING_REGION)
  888 +#if defined(_WIN32)
846 889 InitializeCriticalSection(&async_cb_lock);
847 890 async_cb_cond = CreateEvent(NULL, FALSE, FALSE, NULL);
848 891 #endif
107 ext/ffi_c/Thread.c
@@ -180,6 +180,113 @@ rbffi_thread_blocking_region(VALUE (*func)(void *), void *data1, void (*ubf)(voi
180 180 }
181 181
182 182 #else
  183 +/* win32 implementation */
  184 +
  185 +struct BlockingThread {
  186 + HANDLE tid;
  187 + VALUE (*fn)(void *);
  188 + void *data;
  189 + void (*ubf)(void *);
  190 + void *data2;
  191 + VALUE retval;
  192 + int wrfd;
  193 + int rdfd;
  194 +};
  195 +
  196 +static DWORD __stdcall
  197 +rbffi_blocking_thread(LPVOID args)
  198 +{
  199 + struct BlockingThread* thr = (struct BlockingThread *) args;
  200 + char c = 1;
  201 + VALUE retval;
  202 +
  203 + retval = (*thr->fn)(thr->data);
  204 + thr->retval = retval;
  205 +
  206 + write(thr->wrfd, &c, sizeof(c));
  207 +
  208 + return 0;
  209 +}
  210 +
  211 +static VALUE
  212 +wait_for_thread(void *data)
  213 +{
  214 + struct BlockingThread* thr = (struct BlockingThread *) data;
  215 + char c, res;
  216 + fd_set rfds;
  217 +
  218 + FD_ZERO(&rfds);
  219 + FD_SET(thr->rdfd, &rfds);
  220 + rb_thread_select(thr->rdfd + 1, &rfds, NULL, NULL, NULL);
  221 + read(thr->rdfd, &c, 1);
  222 + return Qnil;
  223 +}
  224 +
  225 +static VALUE
  226 +cleanup_blocking_thread(void *data, VALUE exc)
  227 +{
  228 + struct BlockingThread* thr = (struct BlockingThread *) data;
  229 +
  230 + if (thr->ubf != (void (*)(void *)) -1) {
  231 + (*thr->ubf)(thr->data2);
  232 + } else {
  233 + TerminateThread(thr->tid, 0);
  234 + }
  235 +
  236 + return exc;
  237 +}
  238 +
  239 +VALUE
  240 +rbffi_thread_blocking_region(VALUE (*func)(void *), void *data1, void (*ubf)(void *), void *data2)
  241 +{
  242 + struct BlockingThread* thr;
  243 + int fd[2];
  244 + VALUE exc;
  245 + DWORD state;
  246 + DWORD res;
  247 +
  248 + if (_pipe(fd, 1024, O_BINARY) == -1) {
  249 + rb_raise(rb_eSystemCallError, "_pipe() failed");
  250 + return Qnil;
  251 + }
  252 +
  253 + thr = ALLOC_N(struct BlockingThread, 1);
  254 + thr->rdfd = fd[0];
  255 + thr->wrfd = fd[1];
  256 + thr->fn = func;
  257 + thr->data = data1;
  258 + thr->ubf = ubf;
  259 + thr->data2 = data2;
  260 + thr->retval = Qnil;
  261 +
  262 + thr->tid = CreateThread(NULL, 0, rbffi_blocking_thread, thr, 0, NULL);
  263 + if (!thr->tid) {
  264 + close(fd[0]);
  265 + close(fd[1]);
  266 + xfree(thr);
  267 + rb_raise(rb_eSystemCallError, "CreateThread() failed");
  268 + return Qnil;
  269 + }
  270 +
  271 + exc = rb_rescue2(wait_for_thread, (VALUE) thr, cleanup_blocking_thread, (VALUE) thr,
  272 + rb_eException);
  273 +
  274 + /* The thread should be finished, already. */
  275 + WaitForSingleObject(thr->tid, INFINITE);
  276 + CloseHandle(thr->tid);
  277 + close(fd[1]);
  278 + close(fd[0]);
  279 + xfree(thr);
  280 +
  281 + if (exc != Qnil) {
  282 + rb_exc_raise(exc);
  283 + }
  284 +
  285 + return thr->retval;
  286 +}
  287 +
  288 +
  289 +#if 0
183 290
184 291 /*
185 292 * FIXME: someone needs to implement something similar to the posix pipe based

0 comments on commit 23aedab

Please sign in to comment.
Something went wrong with that request. Please try again.