Subject: Re: [ruby-ffi] Re: Passing a String Pointer to Callback Procs -- How to append? |
From: Wayne Meissner |
Date: 11/12/10 12:05 AM |
To: ruby-ffi@googlegroups.com |
Yes, using FFI::Function would work. You still need to keep a strong ref to each one for as long as it is used though - they're garbage collected like everything else, and if the handler gets called by C code after it has been garbage collected, then the VM will have a Really Bad Day. e.g. store the :WRITEFUNCTION FFI::Function in @write_function_ptr connection.setopt(:WRITEFUNCTION, @write_function_ptr = FFI::Function.new(:size_t, [:pointer, :size_t, :size_t,], &self.__method__(_callback))) Thats assuming that Curl::Easy#setopt is not doing that for all the values used as options, which *might* not be an incredibly stupid idea. e.g. module CurlFFI class Easy def setopt(option, value) check_code(CurlFFI.easy_setopt(@pointer, option, @option_values[option] = value)) end end end On 12 November 2010 15:20, Scott Gonyea <gonyea@gmail.com> wrote:
https://github.com/aitrus/streamly_ffi/blob/master/lib/streamly_ffi/base.rb#L120 Scott On Nov 9, 5:56 pm, Wayne Meissner <wmeiss...@gmail.com> wrote:Thank you, Wayne. Evan actually gave me some helpful info, as well, along similar lines (just a little bit before you replied). The fix was to, basically, not make the Proc a constant (first). Then, inside the proc, I would simply append the "stream" variables data to an instance variable. This was then replaced by something NoKarma used (and I stole), which was to pass a FFI::Function, which calls an instance method (which then appends to an instance variable). You can see my code in its current state, which seems to be largely good / stable / etc. There's another issue, which I'm going to post about separately._VERY_ important thing to remember, always keep a reference to the proc returned by data_handler somewhere - e.g. as an @variable in each Request instance. FFI dynamically generates native code for the callback, and you need to keep a strong reference to it to avoid it being prematurely collected, and crashing the VM. Stashing the proc somewhere for the lifetime of the Request instance should suffice. On 9 November 2010 10:30, Scott Gonyea <gon...@gmail.com> wrote:You can't pass ruby objects through C callbacks with FFI, so any variables you want to access from the callback have to be visible to the Proc when you create it. You seem to be mostly doing it correct with your data_handler() method - i.e. the _string parameter to data_handler() will be visible inside the proc callback, and you can just append the data to it. The handler parameter to the callback will not be used (and is most likely nil anyway). I would change the first parameter (stream) of the curl handler to be of type pointer though, and get the data out like so: _string << stream.get_string(0, size) I'm not sure if curl guarantees that the buffer is NUL terminated, but I suspect not, since it is binary data, so you only want to copy out as many bytes as curl indicates are ready (the size param). OneI'm having a really painful time figuring this out. I jumped onto what NoKarma had already begun, with his curl-ffi library, and started running with it. I'm trying to emulate the ruby extension layers used in some different Curl gems (Streamly and Patron, atm).
Curl wants you to pass a method reference, which it will then callback. You can see examples used in Streamly and Patron:
https://github.com/aitrus/streamly/blob/master/ext/streamly.c#L29Streamly, lines 29-83:
https://github.com/aitrus/patron/blob/master/ext/patron/session_ext.c...Patron, lines 59-75 (Much simpler):
Basically, they're allocating a buffer and providing a callback + that buffer, which is sent to the callback. I've added this callback to CurlFFI and I have the Proc working, etc... But I cannot seem to get it to append to the string.
https://github.com/aitrus/curl_ffi/blob/master/lib/bindings.rb#L923 https://github.com/aitrus/curl_ffi/blob/master/lib/bindings.rb#L926Callback Additions to CurlFFI:
I'll delete 926, as it's not necessary (I think).
https://github.com/aitrus/streamly/blob/master/lib/streamly/request.r...Procs that get called:
I suppose the last resort can be to call C libraries, allocating / freeing a string. But that makes me think I'm doing it wrong. I can't imagine that I'm doing anything overly interesting with my code.
Scott