aboutsummaryrefslogtreecommitdiffstats
path: root/mjit.c
diff options
context:
space:
mode:
authork0kubun <k0kubun@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-11-18 08:25:48 +0000
committerk0kubun <k0kubun@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-11-18 08:25:48 +0000
commitfe6974a8fcca42f4f83171097a3bc29fbe0f2f67 (patch)
tree01f157d468dc018c5765144f344e98a37a584c62 /mjit.c
parent0a7a5a7ad4da3ec8bed425502f5012908a0e77c8 (diff)
downloadruby-fe6974a8fcca42f4f83171097a3bc29fbe0f2f67.tar.gz
mjit_worker.c: support MJIT in forked Ruby process
by launching MJIT worker thread in child Ruby process. See the comment before `mjit_child_after_fork` for details. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65785 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'mjit.c')
-rw-r--r--mjit.c63
1 files changed, 50 insertions, 13 deletions
diff --git a/mjit.c b/mjit.c
index 047971f162..25b2bd9322 100644
--- a/mjit.c
+++ b/mjit.c
@@ -496,18 +496,6 @@ init_header_filename(void)
return TRUE;
}
-/* This is called after each fork in the child in to switch off MJIT
- engine in the child as it does not inherit MJIT threads. */
-void
-mjit_child_after_fork(void)
-{
- if (mjit_enabled) {
- verbose(3, "Switching off MJIT in a forked child");
- mjit_enabled = FALSE;
- }
- /* TODO: Should we initiate MJIT in the forked Ruby. */
-}
-
static enum rb_id_table_iterator_result
valid_class_serials_add_i(ID key, VALUE v, void *unused)
{
@@ -661,6 +649,7 @@ mjit_init(struct mjit_options *opts)
verbose(1, "Failure in MJIT header file name initialization\n");
return;
}
+ pch_owner_pid = getpid();
init_list(&unit_queue);
init_list(&active_units);
@@ -748,6 +737,54 @@ mjit_resume(void)
return Qtrue;
}
+/* Skip calling `clean_object_files` for units which currently exist in the list. */
+static void
+skip_cleaning_object_files(struct rb_mjit_unit_list *list)
+{
+ struct rb_mjit_unit *unit = NULL, *next;
+
+ /* No mutex for list, assuming MJIT worker does not exist yet since it's immediately after fork. */
+ list_for_each_safe(&list->head, unit, next, unode) {
+#ifndef _MSC_VER /* Actualy mswin does not reach here since it doesn't have fork */
+ if (unit->o_file) unit->o_file_inherited_p = TRUE;
+#endif
+
+#if defined(_WIN32) /* mswin doesn't reach here either. This is for MinGW. */
+ if (unit->so_file) unit->so_file = NULL;
+#endif
+ }
+}
+
+/* This is called after fork initiated by Ruby's method to launch MJIT worker thread
+ for child Ruby process.
+
+ In multi-process Ruby applications, child Ruby processes do most of the jobs.
+ Thus we want child Ruby processes to enqueue ISeqs to MJIT worker's queue and
+ call the JIT-ed code.
+
+ But unfortunately current MJIT-generated code is process-specific. After the fork,
+ JIT-ed code created by parent Ruby process cannnot be used in child Ruby process
+ because the code could rely on inline cache values (ivar's IC, send's CC) which
+ may vary between processes after fork or embed some process-specific addresses.
+
+ So child Ruby process can't request parent process to JIT an ISeq and use the code.
+ Instead of that, MJIT worker thread is created for all child Ruby processes, even
+ while child processes would end up with compiling the same ISeqs.
+ */
+void
+mjit_child_after_fork(void)
+{
+ if (!mjit_enabled)
+ return;
+
+ /* Let parent process delete the already-compiled object files.
+ This must be done before starting MJIT worker on child process. */
+ skip_cleaning_object_files(&active_units);
+
+ /* MJIT worker thread is not inherited on fork. Start it for this child process. */
+ start_worker();
+}
+
/* Finish the threads processing units and creating PCH, finalize
and free MJIT data. It should be called last during MJIT
life. */
@@ -781,7 +818,7 @@ mjit_finish(void)
rb_native_cond_destroy(&mjit_gc_wakeup);
#ifndef _MSC_VER /* mswin has prebuilt precompiled header */
- if (!mjit_opts.save_temps)
+ if (!mjit_opts.save_temps && getpid() == pch_owner_pid)
remove_file(pch_file);
xfree(header_file); header_file = NULL;