If I pass a function from OCaml to C, and then store a reference to that OCaml function (to call it multiple times) in C do I need to make some special bookkeeping:
a) when I pass the function?
b) when I call it?
I get random segfaults when calling the OCaml closure for let’s say 1000th time. Can there be any race condition that interferes with gc?
GC moves things around in memory. If you save a pointer and return to OCaml, the pointer can very easily become invalid when some future GC is performed. The pointer will point to where the closure used to be, but something different (or nothing) could be there now. You can prevent this by marking the location where you have stored the pointer as a “global root”. Then the GC will update your location to contain the new pointer value.
This is Rule 4 of living in harmony with the garbage collector:
Global variables containing values must be registered with the garbage collector using the caml_register_global_root function.
You can find this in Chapter 19 of the OCaml manual.
It would also solve it, but caml_named_value is not a constant-time operation, so you’d be making every access slower. A generational root is the proper mechanism for storing this pointer (since you won’t be changing the value, you may as well use caml_register_generational_global_root)
void call_caml_f(int arg)
{
static value * closure_f = NULL;
if (closure_f == NULL) {
/* First time around, look up by name */
closure_f = caml_named_value("test function");
}
caml_callback(*closure_f, Val_int(arg));
}
That’s a good point, which I’d forgotten! FWIW, note that Callback.register uses caml_register_global_root rather than caml_register_generational_global_root.