GVL lock and unlock

Hi, I’m coding a Ruby wrapper for libuv (the event-driven library used
in node.js). At some point I cann “uv_run()” which is blocking and
start the reactor loop, so I need to unlock the GVL:

VALUE MyLib_loop_start(VALUE self) {

Allow other Ruby threads to run.

TODO: unlock_GVL function ???

uv_run(uv_default_loop());

At some point (when there is no more events to watch)

uv_run terminates, so I need to take the GVL again.

TODO: lock_GVL function ???

return self;
}

So how should I fill the above GVL functions?
I use Ruby 1.9.3. I expected that GVL_UNLOCK_BEGIN/END [*] could work
but they are not defined in any .h file but in thread.c.

Any tip please? Thanks a lot.

BTW: During the libuv event loop, Ruby callbacks will be called.
Should I take the GVL before invoking them?

[*] http://rxr.whitequark.org/mri/source/thread.c?v=1.9.3#106

2012/4/17 Iñaki Baz C. [email protected]:

So how should I fill the above GVL functions?
I use Ruby 1.9.3. I expected that GVL_UNLOCK_BEGIN/END [*] could work
but they are not defined in any .h file but in thread.c.

Any tip please? Thanks a lot.

BTW: During the libuv event loop, Ruby callbacks will be called.
Should I take the GVL before invoking them?

It seems I should use:

/*

  • rb_thread_blocking_region - permit concurrent/parallel execution.
  • This function does:
  • (1) release GVL.
  •   Other Ruby threads may run in parallel.
    
  • (2) call func with data1.
  • (3) acquire GVL.
  •   Other Ruby threads can not run in parallel any more.
    
  • If another thread interrupts this thread (Thread#kill, signal
    delivery,
  • VM-shutdown request, and so on), ubf()' is called (ubf()’ means
  • “un-blocking function”). ubf()' should interrupt func()’
    execution.
  • There are built-in ubfs and you can specify these ubfs.
  • However, we can not guarantee our built-in ubfs interrupt
  • your `func()’ correctly. Be careful to use
    rb_thread_blocking_region().
  • * RUBY_UBF_IO: ubf for IO operation
    
  • * RUBY_UBF_PROCESS: ubf for process operation
    
  • NOTE: You can not execute most of Ruby C API and touch Ruby
  •     objects in `func()' and `ubf()', including raising an
    
  •     exception, because current thread doesn't acquire GVL
    
  •     (cause synchronization problem).  If you need to do it,
    
  •     read source code of C APIs and confirm by yourself.
    
  • NOTE: In short, this API is difficult to use safely. I recommend
    you
  •     use other ways if you have.  We lack experiences to use this 
    

API.

  •     Please report your problem related on it.
    
  • Safe C API:
  • * rb_thread_interrupted() - check interrupt flag
    
  • * ruby_xalloc(), ruby_xrealloc(), ruby_xfree() -
    
  •     if they called without GVL, acquire GVL automatically.
    

*/

On Tue, Apr 17, 2012 at 3:15 AM, Iaki Baz C. [email protected]
wrote:

BTW: During the libuv event loop, Ruby callbacks will be called.
Should I take the GVL before invoking them?

Yes, definitely, you want to have the GVL released for as little time as
possible, and when it is released you should only be doing things in
memory
you allocated yourself from C.

I ran into similar problems with rb_thread_blocking_region() writing a
wrapper for libev (well, twice now, once with cool.io and once with
nio4r)
as its API mandates a function pointer, as opposed to the sort of
GVL_UNLOCK_BEGIN/END
macros that you would expect. This is a problem, because the libev API
provides before/after callbacks that happen around the system call (e.g.
epoll) that would be great for unlocking and relocking the GVL.

I understand this API was not provided because it was considered too
dangerous, but it was really needed for libev. I wonder if you’ll
encounter
the same problems with libuv. I really wish there were a
GVL_EXTREMELY_DANGEROUS_USE_WITH_CARE_UNLOCK_BEGIN/END macro :wink:

I ended up having to patch the libev source code and put
rb_thread_blocking_region() right into the heart of the event loop.

BTW, what kind of wrapper are you doing for libuv? Are you going for a
comprehensive one, or only wrapping part of the functionality?

I’d love to add libuv into the next release of nio4r (it already uses
libev
for a selector API). libuv could be used to do direct buffers on the C
side
the same way Java NIO can do them on the Java side.

2012/4/24 Iñaki Baz C. [email protected]:

Ok, so in summary:

  • I must release the GVL before running the uv loop (which is a
    blocking function that just returns when there are no listeners in the
    loop). If not, other Ruby threads cannot work (already verified).

So I must use rb_thread_call_without_gvl().

  • And I must take again the GVL when a uv callback occurs (i.e. a
    timer) and I must execute some associated Ruby code (i.e. a
    block.call), and then I must release again the GVL after such a Ruby
    code terminates so uv takes again the control and the GVL is released
    for other Ruby threads to work.

So I must use rb_thread_call_with_gvl().

Err whoops, sorry, didn’t see this while I was at RailsConf.

But yes, you’ve got it! :wink:

Can’t wait to see what you do with libuv. I will probably need to do
something similar with nio4r, but I only want to expose a subset of what
libuv provides.

2012/4/17 Tony A. [email protected]:

On Tue, Apr 17, 2012 at 3:15 AM, Iñaki Baz C. [email protected] wrote:

BTW: During the libuv event loop, Ruby callbacks will be called.
Should I take the GVL before invoking them?

Yes, definitely, you want to have the GVL released for as little time as
possible, and when it is released you should only be doing things in memory
you allocated yourself from C.

Ok, so in summary:

  • I must release the GVL before running the uv loop (which is a
    blocking function that just returns when there are no listeners in the
    loop). If not, other Ruby threads cannot work (already verified).

  • And I must take again the GVL when a uv callback occurs (i.e. a
    timer) and I must execute some associated Ruby code (i.e. a
    block.call), and then I must release again the GVL after such a Ruby
    code terminates so uv takes again the control and the GVL is released
    for other Ruby threads to work.

Am I right?

I ran into similar problems with rb_thread_blocking_region() writing a
wrapper for libev (well, twice now, once with cool.io and once with nio4r)
as its API mandates a function pointer, as opposed to the sort
of GVL_UNLOCK_BEGIN/END macros that you would expect. This is a problem,
because the libev API provides before/after callbacks that happen around the
system call (e.g. epoll) that would be great for unlocking and relocking the
GVL.

I found similar issues and similar complains by reading some threads.

I understand this API was not provided because it was considered too
dangerous, but it was really needed for libev. I wonder if you’ll encounter
the same problems with libuv. I really wish there were a
GVL_EXTREMELY_DANGEROUS_USE_WITH_CARE_UNLOCK_BEGIN/END macro :wink:

Probably yes since uv (when compiled on Linux) is a wrapper of libev :slight_smile:

I ended up having to patch the libev source code and put
rb_thread_blocking_region() right into the heart of the event loop.

Annoying! I think I will advance a bit more in my code (for now
“single Ruby thread” XD) and I will care about this stuff later.

BTW, what kind of wrapper are you doing for libuv? Are you going for a
comprehensive one, or only wrapping part of the functionality?

A comprehensive one. First I took a look to this non mantained project:

GitHub - noderb/noderb: NodeRb = NodeJs - JavaScript + Ruby

(there are thinks that are not very correct there, but it’s nice for
an initial approach).

I’d love to add libuv into the next release of nio4r (it already uses libev
for a selector API).

libuv uses libev, so what would be the purpose? :slight_smile:

libuv could be used to do direct buffers on the C side
the same way Java NIO can do them on the Java side.

I need to go deeper into the libuv API in order to understand what you
mean :slight_smile:

Thanks a lot.

2012/4/28 Tony A. [email protected]:

Can’t wait to see what you do with libuv. I will probably need to do
something similar with nio4r, but I only want to expose a subset of what
libuv provides.

For now it just implements timers (including periodic timers). Finally
I got making the GVL stuff to work, so it allows multithreading :slight_smile:

BTW my aim is to make something like EventMachine, similar API and
features.

When I arrive to TCP/UDP stuf, I will ask you what does your previous
phrase means:

“libuv could be used to do direct buffers on the C side the same way
Java NIO can do them on the Java side.”

:slight_smile: