Project

General

Profile

Misc #13486

Using rb_thread_call_without_gvl{2}

Added by magaudet (Matthew Gaudet) over 3 years ago. Updated over 3 years ago.

Status:
Closed
Priority:
Normal
Assignee:
-
[ruby-core:80800]

Description

I'm currently working on adding asynchronous compilation to Ruby+OMR, and I'm trying to use the existing Ruby thread API. However, given that compilation shouldn't happen while holding the GVL, I've been playing with rb_thread_call_without_gvl{2}. I've encountered something I don't entirely understand however. It appears that if the unblocking function for a thread is actually invoked, the interpreter hangs on shutdown.

With some tracing code elided, it's a pretty simple bit of code:

static int compilation_thread_started = 0;
void unblock_compilation_thread(void* arg) {                                                          
   *(int*)arg  = 0; // interrupt compilation thread.                                                  
}                                                                                                     

void* vm_compile_thread(void *vm) {                                                                   
   while (compilation_thread_started) { // compile until interupted.
         rb_thread_wait_for(rb_time_interval(DBL2NUM(0.01))); // pretend to compile by sleeping.
   }                                                                                                  
   return NULL;                                                                                       
}                                                                                                     

VALUE releaseGVLandStartCompilationThread(rb_vm_t* vm)                                                
   {                                                                                                  
   compilation_thread_started = 1;                                                                    
   rb_thread_call_without_gvl2(vm_compile_thread,             /* func */ 
                               (void*)vm,                     /* func arg */                          
                               unblock_compilation_thread,    /* unblock func */
                               &compilation_thread_started);  /* unblock arg */

   return Qnil; 
   }

void                                                                                                  
kickoff_thread(rb_vm_t* vm)                                                                           
{
   typedef VALUE (*thread_function)(ANYARGS);                                                         
   rb_thread_create((thread_function)(releaseGVLandStartCompilationThread),vm);                       
}

I've attached a patch with a very simple reproducing test case that should apply to trunk as of today. If you run it, what you'll notice is that the unblock function runs, the thread code exits and then the interpreter hangs; in an interpreter, what I see is the spawned thread that released the GVL is waiting to re-aquire, but it appears to be held. The main thread on the other hand, is waiting for the final non-main thread to shut down before proceeding with shutdown.

I've marked this as Misc, because I'm not entirely sure this isn't user error, but I'd love some guidance on how to spawn a thread that's not holding the GVL, but also have it participate in cleanup actions like regular threads.


Files

gvl_thread_error.patch (3.29 KB) gvl_thread_error.patch Patch with testcase. magaudet (Matthew Gaudet), 04/19/2017 07:52 PM

Also available in: Atom PDF