7

I'm working with the NativeCall interface.

The library is going to call my callback function a bunch of times.

That works fine. I can just declare my callback with the right signature, pass it in as &callback and the library calls the sub just fine.

It also has the capability to set a payload void *pointer to anything I want, and it will include that in the call to my callback function.

Can I hide a Perl Str, for example, in the payload and successfully round trip it?

sub set_userdata(Pointer) returns int32 is native { ... }

sub set_callback(&callback(Pointer $userdata --> int32)) returns int32 is native { ... }

sub callback(Pointer $userdata) returns int32 {
    my Str $mystring = ???
    ...
}

my Str $my-userdata-string;

set_userdata(???);
set_callback(&callback);

It seems like it could work with some incantation of binding, "is rw", nativecast() and/or .deref.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
Curt Tilmes
  • 3,035
  • 1
  • 12
  • 24

2 Answers2

9

You can only use a native representation in such a case (such as CStruct, CArray, and CPointer), or alternatively a Blob. You are also responsible for ensuring that you keep a reference to the thing you pass as userdata alive from Perl 6's perspective also, so the GC doesn't reclaim the memory that was passed to the C function.

Memory management is the reason you can't pass any old Perl 6 object off to a C function: there's no way for the GC to know whether the object is still reachable through some C data structure it can't introspect. In a VM like MoarVM objects are moved around in memory over time as part of the garbage collection process also, meaning that the C code could end up with an out-dated pointer.

An alternative strategy is not not pass a pointer at all, but instead pass an integer and use that to index into an array of objects. (That's how the libuv binding inside of MoarVM tracks down the VM-level callbacks, fwiw.)

Jonathan Worthington
  • 29,104
  • 2
  • 97
  • 136
  • I like the idea of an index into a static array I manage on the Perl side. Then a single callback can just grab the right object with that. Thanks! – Curt Tilmes Apr 13 '17 at 20:37
3

I got around this by just ignoring the userdata and making a new closure referencing the Perl object directly for every callback function. Since there is a new closure created every time I set the callback, I think this will leak memory over time.

Curt Tilmes
  • 3,035
  • 1
  • 12
  • 24
  • 2
    Despite Jonathan Worthington coming up with a better approach, I think a shortened example of your attempt would be cool to see in this answer, as people could discuss if your solution had merit, and if it did indeed leak memory. This is a learning site after all. – Jarrod Funnell Apr 13 '17 at 22:53