diff options
author | Takashi Kokubun <[email protected]> | 2021-06-10 00:32:15 -0700 |
---|---|---|
committer | Takashi Kokubun <[email protected]> | 2021-06-10 00:32:24 -0700 |
commit | c5e8a49bdeadd8e424274c17c0d2a9ffed64417b (patch) | |
tree | f8f95bac341505213430cd2b61a846dc7cee089f | |
parent | 791d7d49476e6363c1bd24af637a9581acc627bb (diff) |
Avoid enqueueing the same ISeq twice
by a race condition by multiple Ractors.
Atmically incrementing body->total_calls may have its own cost, so for
now we intentionally leave the unreliable total_calls. So we allow an
ISeq to be never pushed when you use multiple Ractors. However, if you
enqueue a single ccan node twice, get_from_list loops infinitely. Thus
this patch takes care of such a situation.
-rw-r--r-- | common.mk | 1 | ||||
-rw-r--r-- | mjit.c | 30 | ||||
-rw-r--r-- | mjit.h | 2 | ||||
-rw-r--r-- | mjit_worker.c | 2 |
4 files changed, 23 insertions, 12 deletions
@@ -8608,6 +8608,7 @@ mjit.$(OBJEXT): {$(VPATH)}util.h mjit.$(OBJEXT): {$(VPATH)}vm_callinfo.h mjit.$(OBJEXT): {$(VPATH)}vm_core.h mjit.$(OBJEXT): {$(VPATH)}vm_opts.h +mjit.$(OBJEXT): {$(VPATH)}vm_sync.h mjit_build_dir.$(OBJEXT): {$(VPATH)}config.h mjit_build_dir.$(OBJEXT): {$(VPATH)}internal/compiler_is.h mjit_build_dir.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h @@ -23,6 +23,7 @@ #include "internal/file.h" #include "internal/hash.h" #include "internal/warnings.h" +#include "vm_sync.h" #include "mjit_worker.c" @@ -253,18 +254,28 @@ mjit_target_iseq_p(struct rb_iseq_constant_body *body) && !body->builtin_inline_p; } -// This is called from an MJIT worker when worker_p is true. +// If recompile_p is true, the call is initiated by mjit_recompile. +// This assumes the caller holds CRITICAL_SECTION when recompile_p is true. static void -mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info, bool worker_p) +mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info, bool recompile_p) { if (!mjit_enabled || pch_status == PCH_FAILED) return; - if (!mjit_target_iseq_p(iseq->body)) { iseq->body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC; // skip mjit_wait return; } + if (!recompile_p) { + CRITICAL_SECTION_START(3, "in add_iseq_to_process"); + + // This prevents multiple Ractors from enqueueing the same ISeq twice. + if (rb_multi_ractor_p() && iseq->body->jit_func != NOT_ADDED_JIT_ISEQ_FUNC) { + CRITICAL_SECTION_FINISH(3, "in add_iseq_to_process"); + return; + } + } + RB_DEBUG_COUNTER_INC(mjit_add_iseq_to_process); iseq->body->jit_func = (mjit_func_t)NOT_READY_JIT_ISEQ_FUNC; create_unit(iseq); @@ -273,15 +284,12 @@ mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_inf return; if (compile_info != NULL) iseq->body->jit_unit->compile_info = *compile_info; - - if (!worker_p) { - CRITICAL_SECTION_START(3, "in add_iseq_to_process"); - } add_to_list(iseq->body->jit_unit, &unit_queue); if (active_units.length >= mjit_opts.max_cache_size) { unload_requests++; } - if (!worker_p) { + + if (!recompile_p) { verbose(3, "Sending wakeup signal to workers in mjit_add_iseq_to_process"); rb_native_cond_broadcast(&mjit_worker_wakeup); CRITICAL_SECTION_FINISH(3, "in add_iseq_to_process"); @@ -356,9 +364,11 @@ mjit_recompile(const rb_iseq_t *iseq) assert(iseq->body->jit_unit != NULL); if (UNLIKELY(mjit_opts.wait)) { + CRITICAL_SECTION_START(3, "in rb_mjit_recompile_iseq"); remove_from_list(iseq->body->jit_unit, &active_units); add_to_list(iseq->body->jit_unit, &stale_units); - mjit_add_iseq_to_process(iseq, &iseq->body->jit_unit->compile_info, false); + mjit_add_iseq_to_process(iseq, &iseq->body->jit_unit->compile_info, true); + CRITICAL_SECTION_FINISH(3, "in rb_mjit_recompile_iseq"); mjit_wait(iseq->body); } else { @@ -367,7 +377,7 @@ mjit_recompile(const rb_iseq_t *iseq) // It's good to avoid a race condition between mjit_add_iseq_to_process and mjit_compile around jit_unit as well. CRITICAL_SECTION_START(3, "in rb_mjit_recompile_iseq"); iseq->body->jit_unit->stale_p = true; - iseq->body->jit_func = (mjit_func_t)NOT_ADDED_JIT_ISEQ_FUNC; + iseq->body->jit_func = (mjit_func_t)NOT_READY_JIT_ISEQ_FUNC; pending_stale_p = true; CRITICAL_SECTION_FINISH(3, "in rb_mjit_recompile_iseq"); } @@ -20,7 +20,7 @@ // Special address values of a function generated from the // corresponding iseq by MJIT: enum rb_mjit_iseq_func { - // ISEQ was not queued yet for the machine code generation + // ISEQ has never been enqueued to unit_queue yet NOT_ADDED_JIT_ISEQ_FUNC = 0, // ISEQ is already queued for the machine code generation but the // code is not ready yet for the execution diff --git a/mjit_worker.c b/mjit_worker.c index bfcf8c0f69..046d3a9faf 100644 --- a/mjit_worker.c +++ b/mjit_worker.c @@ -1106,7 +1106,7 @@ load_func_from_so(const char *so_file, const char *funcname, struct rb_mjit_unit handle = dlopen(so_file, RTLD_NOW); if (handle == NULL) { mjit_warning("failure in loading code from '%s': %s", so_file, dlerror()); - return (void *)NOT_ADDED_JIT_ISEQ_FUNC; + return (void *)NOT_COMPILED_JIT_ISEQ_FUNC; } func = dlsym(handle, funcname); |