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:
> 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.
>
> 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:
>> 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).
>>
>> One _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:
>>
>>
>>
>> > I'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:
>>
>> > Streamly, lines 29-83:
>> >https://github.com/aitrus/streamly/blob/master/ext/streamly.c#L29
>>
>> > Patron, lines 59-75 (Much simpler):
>> >https://github.com/aitrus/patron/blob/master/ext/patron/session_ext.c...
>>
>> > 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.
>>
>> > Callback Additions to CurlFFI:
>> >https://github.com/aitrus/curl_ffi/blob/master/lib/bindings.rb#L923
>> >https://github.com/aitrus/curl_ffi/blob/master/lib/bindings.rb#L926
>>
>> > I'll delete 926, as it's not necessary (I think).
>>
>> > Procs that get called:
>> >https://github.com/aitrus/streamly/blob/master/lib/streamly/request.r...
>>
>> > 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