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"

Attachments:
http://www.ruby-forum.com/attachment/6812/ActiveWindow.c
http://www.ruby-forum.com/attachment/6813/extconf.rb

···

--
Posted via http://www.ruby-forum.com/.

Hi Adam!

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.

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: ffi/ext/ffi_c/Function.c at master · ffi/ffi · GitHub
(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:

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

···

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

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

···

--
Posted via http://www.ruby-forum.com/.

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 REQ socket on the Ruby thread side.
A minimal example seems to work with just a few lines of code, thanks to
ZMQ.

Andy

···

--
Posted via http://www.ruby-forum.com/.