Understanding Threading

I’m working on a use case where I need to spawn a thread that handles a
blocking piece of communication and have it call back into a lamba
function when the call completes. All without blocking the main
application thread.

The code I have is:

class Foo

def sync(args = {}, &callback)
block = args[:block] || false
if block
Thread.new do
# passing in true says to block until the underlying code ends
@underlying_impl.sync true
callback.call
end
else
@underlying_impl.sync false
callback.call
end
end

end

What I want to have happen is that, if I call this with :block =>
true, the blocking call would get spawned off on a separate thread and
run in parallel to the main thread. And, of course, if called with
:block => false (or without :block at all) that the calling thread would
block until the sync exits.

However, what I’m seeing is that, even with :block => true, the new
thread is created but the main thread is blocking. If I put a
Thread.pass call in just before calling @underlying_impl.sync then the
main thread updates but the other thread never executes.

Am I misunderstanding Threading in Ruby? In Java spawning a Thread has
that other thread run in parallel without interaction. Does Ruby not
work the same way?

Hello Darry,

Is your block using some C extension? In ruby<1.9, threads in ruby do
not use system threads. So, if you call
some external C command, no thread will run in ruby. In ruby>=1.9, it
uses pthreads. Even though, threads in
ruby does not run in parallel. There is a global mutex that makes
every thread runs isolated. Just like a single processor
machine.

In the past, while programming with Qt in Ruby, I did a simple hack in
order to get ruby some processor time when main thread goes loop.

block=Proc.new{ Thread.pass }
timer=Qt::Timer.new(window)
invoke=Qt::BlockInvocation.new(timer, block, "invoke()")
Qt::Object.connect(timer, SIGNAL("timeout()"), invoke, 

SLOT(“invoke()”))
timer.start(10)

Regards,


 Luiz Angelo Daros de Luca, Me.
        [email protected]

2011/6/27 Darryl L. Pierce [email protected]:

On Mon, Jun 27, 2011 at 9:01 AM, Darryl L. Pierce [email protected]
wrote:

block = args[:block] || false
end
thread is created but the main thread is blocking. If I put a
Thread.pass call in just before calling @underlying_impl.sync then the
main thread updates but the other thread never executes.

Am I misunderstanding Threading in Ruby? In Java spawning a Thread has
that other thread run in parallel without interaction. Does Ruby not
work the same way?

The details of how threading in Ruby works really depends on the
implementation of Ruby that you are using.

In MRI Ruby < 1.9, threads were green threads, implemented with the
Ruby process itself. CPU intensive code, and threads that invoke
external C functions will not benefit from these threads (generally).

In MRI Ruby 1.9.x, threads are real system threads. However, there is
still a global interpreter lock that ensures that only one Ruby thread
is running at a time. It is possible for a C extension to be running
it’s code at the same time that Ruby is executing, however, if the C
extension is written to do that.

In JRuby, threads are regular Java threads, and behave the same way.

In Rubinius, threads are like 1.9.x threads, BUT Rubinius is removing
the single global lock, so in the future, the production releases of
Rubinius will have fully concurrent Ruby threads.

Kirk H.
Software Engineer
Engine Y.

On Tue, Jun 28, 2011 at 02:28:07AM +0900, Luiz Angelo Daros de Luca
wrote:

Is your block using some C extension?

Yep, C++.

We have our primary codebase written in C++. We then use Swig to
generate language bindings for Python, Java and Ruby so that we don’t
try to reimplement features in each supported language.

So now the question is whether it’s worth it for 1.8 to try and work
around this issue or whether it’s better to just make the Ruby
developers aware that calling the blocking operations will result in the
main thread being blocked…

In ruby<1.9, threads in ruby do
not use system threads. So, if you call
some external C command, no thread will run in ruby. In ruby>=1.9, it
uses pthreads.

So in 1.9 we should have some semblance of multi-threading with
extensions?

On Jun 27, 2011, at 2:06 PM, Darryl L. Pierce wrote:

So now the question is whether it’s worth it for 1.8 to try and work
around this issue or whether it’s better to just make the Ruby
developers aware that calling the blocking operations will result in the
main thread being blocked…

An alternative, which may or may not be appropriate for your use case,
is to arrange for the C++ part of your application to be executed in a
separate process instead of a separate thread. You’ll need to figure
out the inter-process communication but you’ll gain concurrency since
processes can be scheduled by the OS to run concurrently on different
processors and Ruby’s global lock won’t get in the way.

Gary W.

On Mon, Jun 27, 2011 at 12:06 PM, Darryl L. Pierce [email protected]
wrote:

On Tue, Jun 28, 2011 at 02:28:07AM +0900, Luiz Angelo Daros de Luca wrote:

Is your block using some C extension?

In ruby<1.9, threads in ruby do
not use system threads. So, if you call
some external C command, no thread will run in ruby. In ruby>=1.9, it
uses pthreads.

So in 1.9 we should have some semblance of multi-threading with
extensions?

Yes, if you write your extension to take advantage of it. Do some
searches for information on rb_thread_blocking_region(), and take a
look at the source code for it in thread.c. Here are the relevant
comments:

/*

  • 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) aquire GVL.
  •   Other Ruby threads can not run in parallel any more.
    
  • If another thread interrupts this thread (Thread#kill, signal
    deliverly,
  • 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()' 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.
    

*/

Kirk H.
Software Engineer
Engine Y.