From: Kim Burgestrand Date: 2011-12-06T00:44:30+09:00 Subject: Re: C module calling ruby code cause cfp consistency error --4edce6d8_52d7b105_334 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline Hi Adam=21 On Monday, 5 December 2011 at 11:23, Adam Bukowski wrote: > 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. > =20 > When there is no handler function call in C code - everything is great,= > but it makes no sense. > =20 > Problem is that when I call ruby handler from different thread it cause= s > 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. > =20 > =20 Yes. You cannot call Ruby functions from a thread not created by Ruby. Wh= en 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 t= he GIL. You *can* work around this, but it is not easy. But before you try workin= g around it with the solution I am about to (try to) explain, I urge you to read t= he later parts of my post about =60rb=5Fthread=5Fblocking=5Fregion=60. Anyway, Dealing with this kind of asynchronous callback in Ruby means you will ne= ed 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 impl= emented in C, as you will need to convert all the data you need from the callback= to Ruby VALUEs from C values. Because the =60call=5Fhandler=60 is in a non-ruby thread, it cannot call = ruby functions. So, to pass information when this handler is called you need to have some= thing that *can* call Ruby functions wait for the =60call=5Fhandler=60 to be ca= lled, and then grab data from =60call=5Fhandler=60, convert it to Ruby values and then u= se it however you want. The only way I know of doing this is by setting up a global condition var= iable that, from a Ruby-created thread, you wait for signals on (make sure you use th= e =60rb=5F- thread=5Fblocking=5Fregion=60 function or you will block all of Ruby). Wh= en =60call=5Fhandler=60 is called you put the title in a memory location accessible from both the= waiting C function from Ruby, and =60call=5Fhandler=60 itself. =60call=5Fhandler=60 will then put the raw title (or any kind of informat= ion) in this shared memory location and then signal the Ruby C waiting-function that the call= back has been called. Once it has signaled, this =60call=5Fhandler=60 can exit. Ju= st make sure that you don=E2=80=99t lose the parameters from memory when you exit the =60ca= ll=5Fhandler=60. 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 me= mory loc- ation and convert it to Ruby VALUEs that you can use in your Ruby code. I=E2=80=99m sorry if this seems confusing. I=E2=80=99ve tried explaining = this before by writing a blog post about it: http://burgestrand.se/articles/asynchronous-callbacks-in-r= uby-c-extensions.html (keep in mind, the code might not work on windows, it is only for explana= tion of concept) If you need another implementation, you can also look at Ruby =46=46I, th= at does a very similar thing to handle callbacks: https://github.com/ffi/ffi/blob/master= /ext/ffi=5Fc/=46unction.c (look for words gvl, cb, async; do keep in mind Ruby =46=46I=E2=80=99s so= lution works on windows, ruby 1.8.7 and is a more general solution than what I have in my blog pos= t) > 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. > =20 > =20 If you try a blocking operation in any Ruby thread it will not allow othe= r threads to run. This, again, is because of the GIL. If you need to do a blocking operatio= n in C, you can use =60rb=5Fthread=5Fblocking=5Fregion=60 to do it: https://github.com/ruby/ruby/blob/ruby=5F1=5F9=5F3/thread.c=23L1079 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 pas= s additional data you can use the second parameter, which will be given to the blocking fun= ction as its=E2=80=99 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 paramete= r, with the fourth parameter as its=E2=80=99 argument. I wish you the best of luck, Adam=21 PS: You might find =60rb=5Fthread=5Fcall=5Fwith=5Fgvl=60 when you look th= rough the source, and it might look promising. Problem with this function is that it is useless wh= en you are in a non-Ruby thread. It is only useful if you are within a function that you = came into by using =60rb=5Fthread=5Fblocking=5Fregion=60, to temporarily jump back into Ruby= and do some operation. =E2=80=94 Kim Burgestrand --4edce6d8_52d7b105_334--