ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:80800] [Ruby trunk Misc#13486] Using rb_thread_call_without_gvl{2}
       [not found] <redmine.issue-13486.20170419200844@ruby-lang.org>
@ 2017-04-19 20:08 ` magaudet
  2017-04-19 23:13 ` [ruby-core:80801] " ko1
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 4+ messages in thread
From: magaudet @ 2017-04-19 20:08 UTC (permalink / raw
  To: ruby-core

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

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

* Author: magaudet (Matthew Gaudet)
* Status: Open
* 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/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [ruby-core:80801] [Ruby trunk Misc#13486] Using rb_thread_call_without_gvl{2}
       [not found] <redmine.issue-13486.20170419200844@ruby-lang.org>
  2017-04-19 20:08 ` [ruby-core:80800] [Ruby trunk Misc#13486] Using rb_thread_call_without_gvl{2} magaudet
@ 2017-04-19 23:13 ` ko1
  2017-04-20 12:54 ` [ruby-core:80809] " magaudet
  2017-04-21 15:27 ` [ruby-core:80818] " magaudet
  3 siblings, 0 replies; 4+ messages in thread
From: ko1 @ 2017-04-19 23:13 UTC (permalink / raw
  To: ruby-core

Issue #13486 has been updated by ko1 (Koichi Sasada).


I'm not sure what is the problem on your situation, but you can't call `rb_thread_wait_for` when you don't holding GVL.
Basically, you can't use any of `rb_...` APIs because they depend on GVL.

BTW, I'm not sure why you are using Ruby thread for a compilation thread.
You can ignore all of Ruby mechanism.

Thanks,
Koichi


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

* Author: magaudet (Matthew Gaudet)
* Status: Open
* 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/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [ruby-core:80809] [Ruby trunk Misc#13486] Using rb_thread_call_without_gvl{2}
       [not found] <redmine.issue-13486.20170419200844@ruby-lang.org>
  2017-04-19 20:08 ` [ruby-core:80800] [Ruby trunk Misc#13486] Using rb_thread_call_without_gvl{2} magaudet
  2017-04-19 23:13 ` [ruby-core:80801] " ko1
@ 2017-04-20 12:54 ` magaudet
  2017-04-21 15:27 ` [ruby-core:80818] " magaudet
  3 siblings, 0 replies; 4+ messages in thread
From: magaudet @ 2017-04-20 12:54 UTC (permalink / raw
  To: ruby-core

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


Ok. That makes sense. 

Thanks for the tip. This can probably get closed (I can't seem to do it myself).

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

* Author: magaudet (Matthew Gaudet)
* Status: Open
* 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/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [ruby-core:80818] [Ruby trunk Misc#13486] Using rb_thread_call_without_gvl{2}
       [not found] <redmine.issue-13486.20170419200844@ruby-lang.org>
                   ` (2 preceding siblings ...)
  2017-04-20 12:54 ` [ruby-core:80809] " magaudet
@ 2017-04-21 15:27 ` magaudet
  3 siblings, 0 replies; 4+ messages in thread
From: magaudet @ 2017-04-21 15:27 UTC (permalink / raw
  To: ruby-core

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/

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2017-04-21 14:47 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <redmine.issue-13486.20170419200844@ruby-lang.org>
2017-04-19 20:08 ` [ruby-core:80800] [Ruby trunk Misc#13486] Using rb_thread_call_without_gvl{2} magaudet
2017-04-19 23:13 ` [ruby-core:80801] " ko1
2017-04-20 12:54 ` [ruby-core:80809] " magaudet
2017-04-21 15:27 ` [ruby-core:80818] " magaudet

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).