From f8b761c62f177176dfe154a95824576eef740827 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 21 Jun 2023 19:07:00 +0100 Subject: [PATCH 08/16] ocaml: Replace caml_leave_blocking_section with caml_acquire_runtime_system Replace: caml_leave_blocking_section ... caml_enter_blocking_section with the more sensibly named (and equivalent): caml_acquire_runtime_system ... caml_release_runtime_system In addition we must release the runtime system just after caml_startup and only acquire it around callbacks into OCaml code. (The reason for this is only apparent in a later commit.) OCaml 5 is more strict than earlier versions of OCaml and actually implements the locks using pthread_mutex_t. So in OCaml 5 you will see a deadlock error: Fatal error: Fatal error during lock: Resource deadlock avoided which was caused by attempting to double lock (ie. trying to acquire the lock, when we already held it from caml_startup). Also because of additional strictness we must acquire the lock even before creating the stack frame with CAMLparam* functions. --- plugins/ocaml/plugin.h | 16 ++++----- plugins/ocaml/plugin.c | 75 +++++++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 42 deletions(-) diff --git a/plugins/ocaml/plugin.h b/plugins/ocaml/plugin.h index 572c43c04..3d6d27db1 100644 --- a/plugins/ocaml/plugin.h +++ b/plugins/ocaml/plugin.h @@ -47,18 +47,18 @@ caml_alloc_initialized_string (mlsize_t len, const char *p) #endif /* For functions which call into OCaml code, call - * caml_leave_blocking_section() to prevent other threads running, - * then caml_enter_blocking_section() on return to C code. This macro - * ensures that the calls are paired properly. + * caml_acquire_runtime_system ... caml_release_runtime_system around + * the code. This prevents other threads in the same domain running. + * The macro ensures that the calls are paired properly. */ -#define LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE() \ - __attribute__ ((unused, cleanup (cleanup_enter_blocking_section))) \ +#define ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE() \ + __attribute__ ((unused, cleanup (cleanup_release_runtime_system))) \ int _unused; \ - caml_leave_blocking_section () + caml_acquire_runtime_system () static inline void -cleanup_enter_blocking_section (int *unused) +cleanup_release_runtime_system (int *unused) { - caml_enter_blocking_section (); + caml_release_runtime_system (); } #endif /* NBDKIT_OCAML_PLUGIN_H */ diff --git a/plugins/ocaml/plugin.c b/plugins/ocaml/plugin.c index 722b95e49..fe781f9af 100644 --- a/plugins/ocaml/plugin.c +++ b/plugins/ocaml/plugin.c @@ -64,6 +64,12 @@ constructor (void) /* Initialize OCaml runtime. */ caml_startup (argv); + + /* We need to release the runtime system here so other threads may + * use it. Before we call any OCaml callbacks we must acquire the + * runtime system again. + */ + caml_release_runtime_system (); } /* Instead of using the NBDKIT_REGISTER_PLUGIN macro, we construct the @@ -113,7 +119,7 @@ plugin_init (void) static void load_wrapper (void) { - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); caml_callback (load_fn, Val_unit); } @@ -123,8 +129,9 @@ load_wrapper (void) static void unload_wrapper (void) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); + if (unload_fn) { - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); caml_callback (unload_fn, Val_unit); } @@ -139,9 +146,9 @@ unload_wrapper (void) static void dump_plugin_wrapper (void) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (dump_plugin_fn, Val_unit); if (Is_exception_result (rv)) @@ -152,9 +159,9 @@ dump_plugin_wrapper (void) static int config_wrapper (const char *key, const char *val) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal3 (keyv, valv, rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); keyv = caml_copy_string (key); valv = caml_copy_string (val); @@ -171,9 +178,9 @@ config_wrapper (const char *key, const char *val) static int config_complete_wrapper (void) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (config_complete_fn, Val_unit); if (Is_exception_result (rv)) { @@ -187,9 +194,9 @@ config_complete_wrapper (void) static int thread_model_wrapper (void) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (thread_model_fn, Val_unit); if (Is_exception_result (rv)) { @@ -203,9 +210,9 @@ thread_model_wrapper (void) static int get_ready_wrapper (void) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (get_ready_fn, Val_unit); if (Is_exception_result (rv)) { @@ -219,9 +226,9 @@ get_ready_wrapper (void) static int after_fork_wrapper (void) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (after_fork_fn, Val_unit); if (Is_exception_result (rv)) { @@ -235,9 +242,9 @@ after_fork_wrapper (void) static void cleanup_wrapper (void) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (cleanup_fn, Val_unit); if (Is_exception_result (rv)) { @@ -251,9 +258,9 @@ cleanup_wrapper (void) static int preconnect_wrapper (int readonly) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (preconnect_fn, Val_bool (readonly)); if (Is_exception_result (rv)) { @@ -267,9 +274,9 @@ preconnect_wrapper (int readonly) static int list_exports_wrapper (int readonly, int is_tls, struct nbdkit_exports *exports) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal2 (rv, v); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback2_exn (list_exports_fn, Val_bool (readonly), Val_bool (is_tls)); @@ -299,10 +306,10 @@ list_exports_wrapper (int readonly, int is_tls, struct nbdkit_exports *exports) static const char * default_export_wrapper (int readonly, int is_tls) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); const char *name; - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback2_exn (default_export_fn, Val_bool (readonly), Val_bool (is_tls)); @@ -318,10 +325,10 @@ default_export_wrapper (int readonly, int is_tls) static void * open_wrapper (int readonly) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); value *ret; - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (open_fn, Val_bool (readonly)); if (Is_exception_result (rv)) { @@ -341,9 +348,9 @@ open_wrapper (int readonly) static void close_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (close_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -360,10 +367,10 @@ close_wrapper (void *h) static const char * export_description_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); const char *desc; - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (export_description_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -378,10 +385,10 @@ export_description_wrapper (void *h) static int64_t get_size_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); int64_t r; - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (get_size_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -397,11 +404,11 @@ static int block_size_wrapper (void *h, uint32_t *minimum, uint32_t *preferred, uint32_t *maximum) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); int i; int64_t i64; - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (block_size_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -439,9 +446,9 @@ block_size_wrapper (void *h, static int can_write_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (can_write_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -455,9 +462,9 @@ can_write_wrapper (void *h) static int can_flush_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (can_flush_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -471,9 +478,9 @@ can_flush_wrapper (void *h) static int is_rotational_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (is_rotational_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -487,9 +494,9 @@ is_rotational_wrapper (void *h) static int can_trim_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (can_trim_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -503,9 +510,9 @@ can_trim_wrapper (void *h) static int can_zero_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (can_zero_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -519,9 +526,9 @@ can_zero_wrapper (void *h) static int can_fua_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (can_fua_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -535,9 +542,9 @@ can_fua_wrapper (void *h) static int can_fast_zero_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (can_fast_zero_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -551,9 +558,9 @@ can_fast_zero_wrapper (void *h) static int can_cache_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (can_cache_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -567,9 +574,9 @@ can_cache_wrapper (void *h) static int can_extents_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (can_extents_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -583,9 +590,9 @@ can_extents_wrapper (void *h) static int can_multi_conn_wrapper (void *h) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal1 (rv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); rv = caml_callback_exn (can_multi_conn_fn, *(value *) h); if (Is_exception_result (rv)) { @@ -629,10 +636,10 @@ static int pread_wrapper (void *h, void *buf, uint32_t count, uint64_t offset, uint32_t flags) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal4 (rv, countv, offsetv, flagsv); mlsize_t len; - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); countv = Val_int (count); offsetv = caml_copy_int64 (offset); @@ -659,9 +666,9 @@ static int pwrite_wrapper (void *h, const void *buf, uint32_t count, uint64_t offset, uint32_t flags) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal4 (rv, strv, offsetv, flagsv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); strv = caml_alloc_initialized_string (count, buf); offsetv = caml_copy_int64 (offset); @@ -680,9 +687,9 @@ pwrite_wrapper (void *h, const void *buf, uint32_t count, uint64_t offset, static int flush_wrapper (void *h, uint32_t flags) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal2 (rv, flagsv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); flagsv = Val_flags (flags); @@ -698,9 +705,9 @@ flush_wrapper (void *h, uint32_t flags) static int trim_wrapper (void *h, uint32_t count, uint64_t offset, uint32_t flags) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal4 (rv, countv, offsetv, flagsv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); countv = caml_copy_int64 (count); offsetv = caml_copy_int64 (offset); @@ -719,9 +726,9 @@ trim_wrapper (void *h, uint32_t count, uint64_t offset, uint32_t flags) static int zero_wrapper (void *h, uint32_t count, uint64_t offset, uint32_t flags) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal4 (rv, countv, offsetv, flagsv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); countv = caml_copy_int64 (count); offsetv = caml_copy_int64 (offset); @@ -741,9 +748,9 @@ static int extents_wrapper (void *h, uint32_t count, uint64_t offset, uint32_t flags, struct nbdkit_extents *extents) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal5 (rv, countv, offsetv, flagsv, v); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); countv = caml_copy_int64 (count); offsetv = caml_copy_int64 (offset); @@ -781,9 +788,9 @@ extents_wrapper (void *h, uint32_t count, uint64_t offset, uint32_t flags, static int cache_wrapper (void *h, uint32_t count, uint64_t offset, uint32_t flags) { + ACQUIRE_RUNTIME_FOR_CURRENT_SCOPE (); CAMLparam0 (); CAMLlocal4 (rv, countv, offsetv, flagsv); - LEAVE_BLOCKING_SECTION_FOR_CURRENT_SCOPE (); countv = caml_copy_int64 (count); offsetv = caml_copy_int64 (offset); -- 2.41.0