[前][次][番号順一覧][スレッド一覧]

ruby-changes:64403

From: Takashi <ko1@a...>
Date: Mon, 21 Dec 2020 16:00:01 +0900 (JST)
Subject: [ruby-changes:64403] 1fdc97f1b7 (master): Mark active_units

https://git.ruby-lang.org/ruby.git/commit/?id=1fdc97f1b7

From 1fdc97f1b76b7532d011b20d52f843a2bb0d1a2f Mon Sep 17 00:00:00 2001
From: Takashi Kokubun <takashikkbn@g...>
Date: Sun, 20 Dec 2020 22:41:52 -0800
Subject: Mark active_units

to avoid SEGV on mjit_recompile and compact_all_jit_code.

For some reason, ISeqs on stack are sometimes GC-ed (why?) and therefore
it may run mjit_recompile on a GC-ed ISeq, which I expected d07183ec85d
to fix but apparently it may refer to random things if already GC-ed.
Marking active_units would workaround the situation.
http://ci.rvm.jp/results/trunk-mjit-wait@phosphorus-docker/3292740

Also, while compact_all_jit_code was executed, we saw some SEGVs where
CCs seemed to be already GC-ed, meaning their owner ISeq was not marked
properly. Even if units are still in active_units, it's not guaranteed
that their ISeqs are in use. So in this case we need to mark active_units
for a legitimate reason.
http://ci.rvm.jp/results/trunk-mjit-wait@phosphorus-docker/3293277
http://ci.rvm.jp/results/trunk-mjit-wait@phosphorus-docker/3293090

diff --git a/mjit.c b/mjit.c
index 5c2042b..1b0fc68 100644
--- a/mjit.c
+++ b/mjit.c
@@ -350,10 +350,7 @@ mjit_recompile(const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/mjit.c#L350
 
     verbose(1, "JIT recompile: %s@%s:%d", RSTRING_PTR(iseq->body->location.label),
             RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(iseq->body->location.first_lineno));
-    iseq->body->jit_func = (mjit_func_t)NOT_ADDED_JIT_ISEQ_FUNC;
-
-    if (iseq->body->jit_unit == NULL) // mjit_free_iseq is already called
-        return;
+    assert(iseq->body->jit_unit != NULL);
 
     // Lazily move active_units to stale_units to avoid race conditions around active_units with compaction
     CRITICAL_SECTION_START(3, "in rb_mjit_recompile_iseq");
@@ -361,6 +358,7 @@ mjit_recompile(const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/mjit.c#L358
     pending_stale_p = true;
     CRITICAL_SECTION_FINISH(3, "in rb_mjit_recompile_iseq");
 
+    iseq->body->jit_func = (mjit_func_t)NOT_ADDED_JIT_ISEQ_FUNC;
     mjit_add_iseq_to_process(iseq, &iseq->body->jit_unit->compile_info);
     if (UNLIKELY(mjit_opts.wait)) {
         mjit_wait(iseq->body);
@@ -937,6 +935,41 @@ mjit_finish(bool close_handle_p) https://github.com/ruby/ruby/blob/trunk/mjit.c#L935
     verbose(1, "Successful MJIT finish");
 }
 
+// Called by rb_vm_mark() to mark active_units so that we do not GC ISeq which
+// may still be referred to by mjit_recompile() or compact_all_jit_code().
+void
+mjit_mark(void)
+{
+    if (!mjit_enabled)
+        return;
+    RUBY_MARK_ENTER("mjit");
+
+    // We need to release a lock when calling rb_gc_mark to avoid doubly acquiring
+    // a lock by by mjit_gc_start_hook inside rb_gc_mark.
+    //
+    // Because an MJIT worker may modify active_units anytime, we need to convert
+    // the linked list to an array to safely loop its ISeqs without keeping a lock.
+    CRITICAL_SECTION_START(4, "mjit_mark");
+    int length = active_units.length;
+    rb_iseq_t **iseqs = ALLOCA_N(rb_iseq_t *, length);
+
+    struct rb_mjit_unit *unit = NULL;
+    int i = 0;
+    list_for_each(&active_units.head, unit, unode) {
+        iseqs[i] = unit->iseq;
+        i++;
+    }
+    CRITICAL_SECTION_FINISH(4, "mjit_mark");
+
+    for (i = 0; i < length; i++) {
+        if (iseqs[i] == NULL) // ISeq is GC-ed
+            continue;
+        rb_gc_mark((VALUE)iseqs[i]);
+    }
+
+    RUBY_MARK_LEAVE("mjit");
+}
+
 // Called by rb_iseq_mark() to mark cc_entries captured for MJIT
 void
 mjit_mark_cc_entries(const struct rb_iseq_constant_body *const body)
diff --git a/mjit.h b/mjit.h
index 89d9a7a..a523bc9 100644
--- a/mjit.h
+++ b/mjit.h
@@ -98,6 +98,7 @@ extern void mjit_gc_start_hook(void); https://github.com/ruby/ruby/blob/trunk/mjit.h#L98
 extern void mjit_gc_exit_hook(void);
 extern void mjit_free_iseq(const rb_iseq_t *iseq);
 extern void mjit_update_references(const rb_iseq_t *iseq);
+extern void mjit_mark(void);
 extern struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec);
 extern void mjit_cont_free(struct mjit_cont *cont);
 extern void mjit_add_class_serial(rb_serial_t class_serial);
@@ -200,6 +201,7 @@ static inline void mjit_cont_free(struct mjit_cont *cont){} https://github.com/ruby/ruby/blob/trunk/mjit.h#L201
 static inline void mjit_gc_start_hook(void){}
 static inline void mjit_gc_exit_hook(void){}
 static inline void mjit_free_iseq(const rb_iseq_t *iseq){}
+static inline void mjit_mark(void){}
 static inline void mjit_add_class_serial(rb_serial_t class_serial){}
 static inline void mjit_remove_class_serial(rb_serial_t class_serial){}
 static inline VALUE mjit_exec(rb_execution_context_t *ec) { return Qundef; /* unreachable */ }
diff --git a/vm.c b/vm.c
index 0c2ffd9..ee28a2d 100644
--- a/vm.c
+++ b/vm.c
@@ -2594,6 +2594,8 @@ rb_vm_mark(void *ptr) https://github.com/ruby/ruby/blob/trunk/vm.c#L2594
 	rb_gc_mark_values(RUBY_NSIG, vm->trap_list.cmd);
 
         rb_id_table_foreach_values(vm->negative_cme_table, vm_mark_negative_cme, NULL);
+
+        mjit_mark();
     }
 
     RUBY_MARK_LEAVE("vm");
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]