Issue #13486 has been updated by magaudet (Matthew Gaudet).


Thinking more on this, I'm wondering if maybe I would still like to be in a situation where the compilation thread is a ruby thread (and in fact, today, have a working version that runs this way -- I got rid of `rb_thread_wait_for`--, but I run into a test failure. I'll get into that shortly). 

Here's why I think I'd like the compilation thread to be a ruby thread: I would like the process of compiling ruby code the be free to call `rb_` family functions, and perhaps, one day, even execute small pieces of Ruby code during compilation. Today, the JIT already makes `rb_` family calls, though they are relatively few: `rb_iseq_original_iseq`, `rb_class2name` and `rb_id2name` are the big ones today. My understanding is that the thread must be a ruby thread to call these functions. I call them today in the ruby thread I'm using for compilation using `rb_thread_call_with_gvl`. 

While I can run my compilation thread as a Ruby thread, I run into a couple of issues, all of which originate from this thread having user-level visibility: Some tests expect 1 thread, and see 2 and fail, and the deadlock detection sees the compilation thread as a runnable thread so doesn't expect when the last user Thread calls `#stop`. 

What I'd _like_ to do is cloak the compilation thread(s) from a variety of user level thread introspection, and indicate to some VM services this thread is a bit special, but I'm not sure what would be an acceptable way to do this.

The first idea that jumps to mind is adding a bit to rb_thread_t in order to mark a thread as 'hidden', however I'm not sure how well that fits into the ruby-core's view. 




----------------------------------------
Misc #13486: Using rb_thread_call_without_gvl{2}
https://bugs.ruby-lang.org/issues/13486#change-64423

* Author: magaudet (Matthew Gaudet)
* Status: Closed
* Priority: Normal
* Assignee: 
----------------------------------------
I'm currently working on adding asynchronous compilation to [Ruby+OMR][1], 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. 



[1]: https://github.com/rubyomr-preview/ruby/issues/30 

---Files--------------------------------
gvl_thread_error.patch (3.29 KB)


-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>