I’m adding OCaml 5 support to an existing project. The project is just some OCaml bindings and therefore fairly easy to deal with, but it also has callbacks. In other words:
OCaml -> C binding -> callback wrapper -> OCaml
Because most of the C code is long running we release the runtime lock around all C calls. So when we get to a callback wrapper we reacquire the lock around the OCaml code. This part all works fine.
However there is a C atexit function which does some cleanup on process exit. Unfortunately during the exit path, callbacks can also be invoked. The path is:
I don’t know the answer to your question, but am I right that this used to work in OCaml 4.x? (That is, it was possible to acquire the runtime lock more than once.)
If yes, shouldn’t this be considered a bug in OCaml 5, given that one of its guiding principles is that FFI code that used to work in OCaml 4.x should continue to work in OCaml 5?
I don’t know of any documentation regarding this point, but I remember seeing something very similar to what is reported by @rwmjones in production code.
I have implemented Caml_state_opt for this purpose in OCaml 5.0, due to a similar problem. Per the manual:
The macro Caml_state evaluates to the domain state variable, and checks in debug mode that the domain lock is held. Such a check is also placed in normal mode at key entry points of the C API; this is why calling some of the runtime functions and macros without correctly owning the domain lock can result in a fatal error: no domain lock held. The variant Caml_state_opt does not perform any check but evaluates to NULL when the domain lock is not held. This lets you determine whether a thread belonging to a domain currently holds its domain lock, for various purposes.
It was definitely unsafe, and was in fact part of the motivations to let caml_state be NULL when the domain lock is not held and to add Caml_state_opt to let users check for this.
works but only on platforms where caml_state can be accessed directly (in which case it is equal to Caml_state_opt). Prefer Caml_state_opt for portability.