ocaml: Conditionally acquire the lock in callbacks

This fix was originally suggested by Jürgen Hötzel (link below) which
I have lightly modified so it works with OCaml <= 4 too.

Link: https://listman.redhat.com/archives/libguestfs/2023-May/031640.html
Link: https://discuss.ocaml.org/t/test-caml-state-and-conditionally-caml-acquire-runtime-system-good-or-bad/12489
This commit is contained in:
Richard W.M. Jones
2023-06-27 12:09:12 +01:00
parent 4a79c023e5
commit 16464878cf

View File

@@ -19,6 +19,7 @@
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
@@ -36,6 +37,7 @@
#include <caml/signals.h>
#include <caml/threads.h>
#include <caml/unixsupport.h>
#include <caml/version.h>
#include "guestfs-c.h"
@@ -397,12 +399,31 @@ event_callback_wrapper (guestfs_h *g,
{
/* Ensure we are holding the GC lock before any GC operations are
* possible. (RHBZ#725824)
*
* There are many paths where we already hold the OCaml lock before
* this function, for example "non-blocking" calls, and the
* libguestfs global atexit path (which calls guestfs_close). To
* avoid double acquisition we need to check if we already hold the
* lock. OCaml 5 is strict about this. In earlier OCaml versions
* there is no way to check, but they did not implement the lock as
* a mutex and so it didn't cause problems.
*
* See also:
* https://discuss.ocaml.org/t/test-caml-state-and-conditionally-caml-acquire-runtime-system-good-or-bad/12489
*/
#if OCAML_VERSION_MAJOR >= 5
bool acquired = caml_state != NULL;
#else
const bool acquired = false;
#endif
if (!acquired)
caml_acquire_runtime_system ();
event_callback_wrapper_locked (g, data, event, event_handle, flags,
buf, buf_len, array, array_len);
if (!acquired)
caml_release_runtime_system ();
}