C module calling ruby code cause cfp consistency error

Hi everyone,

I need some help with joining C code with Ruby.
My target is to use some system libraries to get active window title and
write rest in ruby.
I’ve written some module in C that uses wnck(on linux) to get window
title. There must be some event loop which I run in different thread.
When event window-changed occurs i want to call ruby method, to report
window title.
I’ve used some kind of observer pattern here, so ruby registers handler
method in C module, and do something else.

When there is no handler function call in C code - everything is great,
but it makes no sense.

Problem is that when I call ruby handler from different thread it causes
cfp consistency error and rest of code in ruby file isn’t working
properly. It hangs waiting for window-change. When window is changed
then it goes to next instruction.

I’ve tried not to use native thread and call this blocking wnck’s loop
in ruby’s Thread.new, but it hangs totally then.

I’ve stuck.

Ruby code is here, rest in attachments.

Please give me support and some comments about handling cross threads
calls.

Thanks in advance,
Adam

require ‘ActiveWindow’
include ActiveWindow

class TitleHandler
def initialize
@mutex = Mutex.new
end
def handle(title = ‘’, wclass = ‘’)
@mutex.synchronize {
print title + " - " + wclass + “\n”
}
end
end

handler = TitleHandler.new
ActiveWindow.register_handler(handler, “handle”)
ActiveWindow.start_loop

puts “wait…”
sleep 1
puts “1”
sleep 1
puts “2”
sleep 1
puts “3”
ActiveWindow.stop_loop
puts “end”

Hi Adam!

On Monday, 5 December 2011 at 11:23, Adam Bukowski wrote:

Problem is that when I call ruby handler from different thread it causes
cfp consistency error and rest of code in ruby file isn’t working
properly. It hangs waiting for window-change. When window is changed
then it goes to next instruction.

Yes. You cannot call Ruby functions from a thread not created by Ruby.
When
it comes to MRI, you must be the owner of the GIL to call ruby
functions, and
since the thread is not a ruby-created thread you have no way of owning
the GIL.

You can work around this, but it is not easy. But before you try
working around
it with the solution I am about to (try to) explain, I urge you to read
the later parts
of my post about rb_thread_blocking_region.

Anyway,

Dealing with this kind of asynchronous callback in Ruby means you will
need
a thread in Ruby (either main thread, or just another thread, we call it
the callback
thread) waiting for the callback to happen. This waiting needs to be
implemented
in C, as you will need to convert all the data you need from the
callback to Ruby
VALUEs from C values.

Because the call_handler is in a non-ruby thread, it cannot call ruby
functions.
So, to pass information when this handler is called you need to have
something
that can call Ruby functions wait for the call_handler to be called,
and then
grab data from call_handler, convert it to Ruby values and then use it
however
you want.

The only way I know of doing this is by setting up a global condition
variable that,
from a Ruby-created thread, you wait for signals on (make sure you use
the rb_- thread_blocking_region function or you will block all of Ruby). When
call_handler
is called you put the title in a memory location accessible from both
the waiting C
function from Ruby, and call_handler itself.

call_handler will then put the raw title (or any kind of information)
in this shared
memory location and then signal the Ruby C waiting-function that the
callback has
been called. Once it has signaled, this call_handler can exit. Just
make sure that
you don’t lose the parameters from memory when you exit the
call_handler.

Anyhow, now when Ruby C waiting function has been signaled it knows that
it has
some data. It will take this data from the previously mentioned shared
memory loc-
ation and convert it to Ruby VALUEs that you can use in your Ruby code.

I’m sorry if this seems confusing. I’ve tried explaining this before by
writing a blog
post about it:
http://burgestrand.se/articles/asynchronous-callbacks-in-ruby-c-extensions.html
(keep in mind, the code might not work on windows, it is only for
explanation of concept)

If you need another implementation, you can also look at Ruby FFI, that
does a very
similar thing to handle callbacks:
https://github.com/ffi/ffi/blob/master/ext/ffi_c/Function.c
(look for words gvl, cb, async; do keep in mind Ruby FFI’s solution
works on windows,
ruby 1.8.7 and is a more general solution than what I have in my blog
post)

I’ve tried not to use native thread and call this blocking wnck’s loop
in ruby’s Thread.new, but it hangs totally then.

If you try a blocking operation in any Ruby thread it will not allow
other threads to run.
This, again, is because of the GIL. If you need to do a blocking
operation in C, you can
use rb_thread_blocking_region to do it:

https://github.com/ruby/ruby/blob/ruby_1_9_3/thread.c#L1079

This method will first unlock the GIL, allowing other Ruby threads to
run, and then call
your blocking operation (supplied as first parameter). If you need to
pass additional data
you can use the second parameter, which will be given to the blocking
function as its’
argument.

If Ruby needs to abort the blocking function (any reason, for example if
user tries to exit
your script) it will call the unblocking function given as third
parameter, with the fourth
parameter as its’ argument.

I wish you the best of luck, Adam!

PS: You might find rb_thread_call_with_gvl when you look through the
source, and it
might look promising. Problem with this function is that it is useless
when you are in a
non-Ruby thread. It is only useful if you are within a function that you
came into by using
rb_thread_blocking_region, to temporarily jump back into Ruby and do
some operation.

— Kim Burgestrand

Hi

I’m currently developing an ruby extension which confronts me with the
same problem: I should be a able to forward a callback in the C code
from another thread to the ruby thread.

I choose to use ZMQ (http://zeromq.org/) to “cross the thread-gap”: A
REQ socket on the C thread side an a REP socket on the Ruby thread side.
A minimal example seems to work with just a few lines of code, thanks to
ZMQ.

Andy

Really You explained a lot of things to me. I didn’t know basic concepts
about GIL/GVL. To be sincere this is my first program in Ruby. I will
handle this now in some spare time. It’s really nice work - this yours
PoC LMFAO. Really thank for doing this! I wasn’t googling right phrase
so i didn’t find your blog.

Thanks a lot!
Adam Bukowski

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs