Calling Ocaml C FFI using Ctypes pointers

My question:

Is there a well-understood way to mix Ctypes.Foreign data structures with Ocaml C Stubs FFI? I am running into some corruption issues because I think I am doing this translation incorrectly.

Background:

I wrote kqueue bindings using Ctypes.Foreign, however due to some static linking reasons, I have decided to implement the bindings via classic Ocaml C FFI. I would like to continue to use Ctypes to generate bindings for the various structs and constants I need access to, for example the event list structure. Is it well-defined how to use a Ctypes ptr when calling Ocaml C FFI.

Here is the definition of my bindings:

I’m managing my event list via allocate_n

And I am trying to call it using with: (Ctypes.raw_address_of_ptr @@ Ctypes.to_voidp changelist.Eventlist.kevents)

And finally, I try to convert it back to the C representations with:

However, I seem to be getting some corruption at some point in running my program (note, this corruption has not been happening in running with Ctypes.Foriegn for several years, so it IS something to do with what I’m doing here).

I think I’m possibly seeing something around Garbage collection compaction? Although, I’m just guessing. The bad data looks like the following at the syscall level:

15585 759036 terrat_ee.native 287.548822907 CALL kevent(0x4,0x338530457dc0,0x1,0x338531030240,0x400,0x33852d23d5f0)
15585 759036 terrat_ee.native 287.548940822 STRU struct kevent = { { ident=0x5a5a5a5a5a5a5a5a, filter=<invalid=0x5a5a>, flags=0x5a5a<EV_DELETE|EV_DISABLE|EV_KEEPUDATA|EV_ONESHOT|EV_RECEIPT|EV_ERROR>,
fflags=0x5a5a5a5a, data=0x5a5a5a5a5a5a5a5a, udata=0x5a5a5a5a5a5a5a5a } }
15585 759036 terrat_ee.native 287.548961975 STRU struct kevent = { { ident=0x5a5a5a5a5a5a5a5a, filter=<invalid=0x5a5a>, flags=0x4000<EV_ERROR>, fflags=0x5a5a5a5a, data=0x16, udata=0x5a5a5a5a5a5a5a5a } }

void* ctypes_ptr(value v) {

Is this not missing CAMLparam1(v)? And using CAMLreturn?

This is a helper function, not called directly from Ocaml, but from the C code Ocaml is calling. I believe I only have to do the OCaml FFI macros in the function that is being called directly.

Knock on wood, I think I found the solution. After digging around ctypes code it looks like it wraps points in a Fat pointer, which is really just a record with your Ocaml value in it + the pointer value. I’m running our test suite now and it seems to be working.

As long it is not documented/specified, it is like using magic in the code. There is no guarantee that the behavior will stay valid over time.

Once I validated the Fat ptr trick worked, I switched over to my own “fat pointer” implementation so I can be guaranteed the structure won’t change.