Ocaml 5.x FFI differences from 4.14?

Hi, I googled around a bit, and have been unable to find anything that describes the differences in the FFI between OCaml 4.14 and 5.0. I remember there was some discussion a while back about this, but don’t remember where it was.

Is there any sort of description of what’s changed in the FFI?

[I’m trying to upgrade SWIG to work with OCaml 5.x]

2 Likes

In OCaml 5 no-naked-pointers mode is the only mode, while it was an option starting with 4.02. Other than that, the FFI is meant to be preserved. So first port to 4.14 in no-naked-pointers mode, and then it should be compatible with 5 as well. For parallel code, code using the FFI may need to be concerned with the mixed OCaml/C memory model. For that, the best reference I know is a comment in memory.c.

1 Like

Lovely! That’s exactly what I was looking for! Thank you!

Oh, nevermind. The obvious thing worked (it seems): opam switch create 4.14.0 ocaml-option-nnp

Oh, another question: I know I can build an OCaml from-scratch to disable naked pointers mode, and I’ve seen the 4.10.0 nnpcheck (which I’m building right now) but is there a way to build a switch with no-naked-pointers mode? Or should I use the procedure (which I remember seeing in a HACKING document in the OCaml source-tree) for building a custom switch by-hand ?

I see there are these ocaml-variants options like ocaml-option-nnp but I don’t quite understand how they’re supposed to be used wiith opam switch create.

Indeed for parallel code (e.g. any library not clearly marked as single-core-only), the answer is that we don’t know yet. Regarding the mixed memory model, the discussion at OCaml multicore memory model and C (runtime, FFI, VM) · Issue #10992 · ocaml/ocaml · GitHub has a more detail. One option involves deprecating certain uses of Field (in a way that incurs the risk of simply being ignored by programmers), another one involves ultimately evolving the C memory model.

Both paths break existing code, even the second one, for instance if the code contains a duplicated load with the implicit hypothesis that the field does not change (simply marking the load as volatile is not enough to undo the duplication done by the user). This is one consequence of the choice of deciding to make sense of races on mutable fields, which enters here in contradiction with the C memory model.

Note that from a certain point of view, this is not breaking compatibility since single-core programs remain correct. This comes in addition to other things to check to make sure that C code is multicore-ready (e.g. regarding uses of global variables). Does anyone know if there are there efforts to mark libraries (e.g. in opam) as multicore-ready/-not-ready?

There are other changes that affect single-core code. For instance some people used to call caml_remove_generational_global_root inside the finaliser of custom blocks, and one can no longer do this; one should look out for this in code that interface with libraries written in other languages. There are probably other changes (e.g. some pointers now marked const in the FFI). It might be useful to have a list of changes somewhere.

1 Like

Oh yes, I forgot about that one even though it worried me when I saw it go by, thanks.

For multicore-ready FFI watch out for global variables in C code (or static variables inside functions). Previously such usage was safe due to OCaml’s global lock (assuming all accesses were done without releasing the runtime lock), but you can no longer rely on that.
That means also checking the C library code that was called without releasing the OCaml global lock: previously that would’ve only be called from a single thread from OCaml (and assuming no other C threads call them) may have been safe, and now they no longer are.
The manpages of some C functions mention whether they are thread-safe or not.

E.g. strerror has this in its manpage:

       ┌───────────────────────────┬───────────────┬─────────────────────────────────────────────────────────────────────────────────────────┐
       │Interface                  │ Attribute     │ Value                                                                                   │
       ├───────────────────────────┼───────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
       │strerror()                 │ Thread safety │ MT-Unsafe race:strerror                                                                 │
       ├───────────────────────────┼───────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
       │strerrorname_np(),         │ Thread safety │ MT-Safe                                                                                 │
       │strerrordesc_np()          │               │                                                                                         │
       ├───────────────────────────┼───────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
       │strerror_r(), strerror_l() │ Thread safety │ MT-Safe                                                                                 │
       └───────────────────────────┴───────────────┴─────────────────────────────────────────────────────────────────────────────────────────┘

3 Likes