# Relaxed rules for binding a C library?

**URL:** <https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693>\
**Category:** Learning\
**Tags:** multicore\
**Created:** [October 24, 2022, 4:20am UTC](https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693 "2022-10-24T04:20:51Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![alan](https://avatars.discourse-cdn.com/v4/letter/a/a87d85/32.png) [@alan](https://discuss.ocaml.org/u/alan)\
**Post date:** [October 24, 2022, 4:20am UTC](https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693/1 "2022-10-24T04:20:51Z")

</div>

The documentation gives [these rules](https://v2.ocaml.org/manual/intfc.html#ss:c-simple-gc-harmony) for binding a C library.

Is it safe for me to relax the rules with the following assumptions?

1. If a `value` is unboxed and does not contain pointers to the OCaml heap (e.g. it is an int or a bool), it does not need to be registered with `CAMLparam` or `CAMLlocal`. Rationale: The OCaml GC cannot invalidate them and doesn’t need to know them to trace live values.
2. If the function does not allocate on the OCaml heap, it does not need to use `CAMLparam` or `CAMLlocal` at all. Rationale: The GC can only be triggered upon an allocation. Therefore, if the function does not allocate on the OCaml heap, it does not need to register its local variables as roots because the OCaml GC will not run over the duration of the function call. Because the multicore runtime relies on poll points inserted in the compiled OCaml code to context switch, it also cannot preempt a C function.

If these assumptions are correct, is it a good idea for me to write bindings in this way?

---

<div class="post-metadata">

**Author:** ![gadmm](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gadmm/32/877_2.png) [@gadmm](https://discuss.ocaml.org/u/gadmm)\
**Post date:** [October 24, 2022, 10:16am UTC](https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693/2 "2022-10-24T10:16:21Z")

</div>

Your 1. is correct. Your 2. must be strengthened into “the function does not call the GC or release the runtime lock”. There are other ways the GC can run than by calling an allocation function (this is currently partly in undocumented territory).

---

<div class="post-metadata">

**Author:** ![alan](https://avatars.discourse-cdn.com/v4/letter/a/a87d85/32.png) [@alan](https://discuss.ocaml.org/u/alan)\
**Post date:** [October 24, 2022, 12:01pm UTC](https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693/3 "2022-10-24T12:01:27Z")

</div>

Is it a safe assumption that a C function that does not call an OCaml runtime function will not invoke the GC?

---

<div class="post-metadata">

**Author:** ![gadmm](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gadmm/32/877_2.png) [@gadmm](https://discuss.ocaml.org/u/gadmm)\
**Post date:** [October 24, 2022, 12:14pm UTC](https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693/4 "2022-10-24T12:14:24Z")

</div>

Yes​​​​​​​​​​​​​​​​​

---

<div class="post-metadata">

**Author:** ![alan](https://avatars.discourse-cdn.com/v4/letter/a/a87d85/32.png) [@alan](https://discuss.ocaml.org/u/alan)\
**Post date:** [October 24, 2022, 12:16pm UTC](https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693/5 "2022-10-24T12:16:14Z")

</div>

Oh yeah, I have a third rule to ask about. If a function allocates on the OCaml heap, but it does not have any local variables that contain pointers to the OCaml heap, it is also safe to skip calling `CAMLparam` at all, right?

---

<div class="post-metadata">

**Author:** ![gadmm](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gadmm/32/877_2.png) [@gadmm](https://discuss.ocaml.org/u/gadmm)\
**Post date:** [October 24, 2022, 12:17pm UTC](https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693/6 "2022-10-24T12:17:19Z")

</div>

As well​​​​​​​​​​​​​

---

<div class="post-metadata">

**Author:** ![edwin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/edwin/32/627_2.png) [@edwin](https://discuss.ocaml.org/u/edwin)\
**Post date:** [October 24, 2022, 10:14pm UTC](https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693/7 "2022-10-24T22:14:44Z")

</div>

> [@alan](#):
>
> Is it a safe assumption that a C function that does not call an OCaml runtime function will not invoke the GC?

Only if you haven’t registered any callbacks. If someone has registered an OCaml function to be used as a callback by some C code, and you call into a C library which under some circumstances ends up calling this callback then it will run OCaml code while having your C function on the stack.  
This applies transitively (there might be OCaml callbacks registered in other C libraries that your C library indirectly uses), although that’d be a rare case. If your library never registers any callbacks you probably shouldn’t worry about this case.

---

<div class="post-metadata">

**Author:** ![alan](https://avatars.discourse-cdn.com/v4/letter/a/a87d85/32.png) [@alan](https://discuss.ocaml.org/u/alan)\
**Post date:** [October 24, 2022, 11:40pm UTC](https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693/8 "2022-10-24T23:40:52Z")

</div>

Hey, what a coincidence that you decided to show up! What you’re pointing out is actually something that I actually realized I need to worry about…

I’m currently [patching](https://reviews.llvm.org/D136400) the LLVM bindings to be compatible with OCaml 5. The LLVM bindings, as they currently stand, use naked pointers and omit `CAMLparamX` and `CAMLreturn` everywhere, making them unsuitable for the multicore runtime. I have patched them to encode all pointers outside the OCaml heap as OCaml integers, and inserted the necessary `CAMLparamX` and `CAMLreturn` macros.

I had been wondering if I could get away with skipping these macros in some places. However, one of the features of the LLVM bindings is a “diagnostic handler,” and the bindings allow an arbitrary OCaml function to be registered as a handler. I’m not sure which LLVM functions may call the handler, so I have to assume that all of them may trigger the OCaml GC.

Even more concerning is what would happen if the diagnostic handler raised an OCaml exception. It would likely violate many assumptions in the LLVM code, skipping destructors and more.

I’m concerned because two reviewers I’ve tagged have resigned, and I think that there are few people who are deeply familiar with both LLVM and the OCaml runtime. I’m bringing this up because I noticed that you were a contributor to the LLVM bindings, many years ago. Perhaps you could have some expertise to add?

---

<div class="post-metadata">

**Author:** ![edwin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/edwin/32/627_2.png) [@edwin](https://discuss.ocaml.org/u/edwin)\
**Post date:** [October 25, 2022, 8:37am UTC](https://discuss.ocaml.org/t/relaxed-rules-for-binding-a-c-library/10693/9 "2022-10-25T08:37:50Z")

</div>

> [@alan](#):
>
> Even more concerning is what would happen if the diagnostic handler raised an OCaml exception. It would likely violate many assumptions in the LLVM code, skipping destructors and more.

See [OCaml - Interfacing C with OCaml](https://v2.ocaml.org/manual/intfc.html#ss:c-callbacks), in particular this part, the C bindings should probably use caml\_callback\_exn, check the result and then raise a C++ exception to avoid the problem with destructors:

> Blockquote  
> If the C code wishes to catch exceptions escaping the OCaml function, it can use the functions caml\_callback\_exn, caml\_callback2\_exn, caml\_callback3\_exn, caml\_callbackN\_exn. These functions take the same arguments as their non-\_exn counterparts, but catch escaping exceptions and return them to the C code. The return value v of the caml\_callback\*\_exn functions must be tested with the macro Is\_exception\_result(v). If the macro returns “false”, no exception occurred, and v is the value returned by the OCaml function. If Is\_exception\_result(v) returns “true”, an exception escaped, and its value (the exception descriptor) can be recovered using Extract\_exception(v).

I would recommend trying to write a small unit test with the problematic code sequence (set up OCaml diagnostic handler, call some other OCaml functions that would call into the C bindings that would call into this diagnostic handler, and raise an exception), and check it works correctly with the fixed C binding, and then try to see whether you can somehow prove it is not correct with the old code, e.g. by using the OCaml debug runtime, or inserting some code to `Gc.compact` in some places.  
Detecting missing C++ destructors would be more tricky, they might result in a memory leak, so perhaps valgrind could catch something? Or build with the memory sanitizers, I think lately they can catch memory leaks too.
