diff options
author | Koichi Sasada <ko1@atdot.net> | 2023-04-10 10:53:13 +0900 |
---|---|---|
committer | Koichi Sasada <ko1@atdot.net> | 2023-10-12 14:47:01 +0900 |
commit | be1bbd5b7d40ad863ab35097765d3754726bbd54 (patch) | |
tree | 2995a0859bea1d6b2903dcd324f41869dbef14a1 /process.c | |
parent | 096ee0648e215915a3019c2cd68ba220d94eca12 (diff) | |
download | ruby-be1bbd5b7d40ad863ab35097765d3754726bbd54.tar.gz |
M:N thread scheduler for Ractors
This patch introduce M:N thread scheduler for Ractor system.
In general, M:N thread scheduler employs N native threads (OS threads)
to manage M user-level threads (Ruby threads in this case).
On the Ruby interpreter, 1 native thread is provided for 1 Ractor
and all Ruby threads are managed by the native thread.
From Ruby 1.9, the interpreter uses 1:1 thread scheduler which means
1 Ruby thread has 1 native thread. M:N scheduler change this strategy.
Because of compatibility issue (and stableness issue of the implementation)
main Ractor doesn't use M:N scheduler on default. On the other words,
threads on the main Ractor will be managed with 1:1 thread scheduler.
There are additional settings by environment variables:
`RUBY_MN_THREADS=1` enables M:N thread scheduler on the main ractor.
Note that non-main ractors use the M:N scheduler without this
configuration. With this configuration, single ractor applications
run threads on M:1 thread scheduler (green threads, user-level threads).
`RUBY_MAX_CPU=n` specifies maximum number of native threads for
M:N scheduler (default: 8).
This patch will be reverted soon if non-easy issues are found.
[Bug #19842]
Diffstat (limited to 'process.c')
-rw-r--r-- | process.c | 64 |
1 files changed, 35 insertions, 29 deletions
@@ -685,10 +685,16 @@ rb_last_status_set(int status, rb_pid_t pid) GET_THREAD()->last_status = rb_process_status_new(pid, status, 0); } +static void +last_status_clear(rb_thread_t *th) +{ + th->last_status = Qnil; +} + void rb_last_status_clear(void) { - GET_THREAD()->last_status = Qnil; + last_status_clear(GET_THREAD()); } static rb_pid_t @@ -1654,26 +1660,13 @@ before_exec(void) before_exec_async_signal_safe(); } -/* This function should be async-signal-safe. Actually it is. */ static void -after_exec_async_signal_safe(void) -{ -} - -static void -after_exec_non_async_signal_safe(void) +after_exec(void) { rb_thread_reset_timer_thread(); rb_thread_start_timer_thread(); } -static void -after_exec(void) -{ - after_exec_async_signal_safe(); - after_exec_non_async_signal_safe(); -} - #if defined HAVE_WORKING_FORK || defined HAVE_DAEMON static void before_fork_ruby(void) @@ -1686,10 +1679,14 @@ after_fork_ruby(rb_pid_t pid) { rb_threadptr_pending_interrupt_clear(GET_THREAD()); if (pid == 0) { + // child clear_pid_cache(); rb_thread_atfork(); } - after_exec(); + else { + // parent + after_exec(); + } } #endif @@ -4210,16 +4207,19 @@ rb_fork_ruby2(struct rb_process_status *status) while (1) { prefork(); - disable_child_handler_before_fork(&old); + before_fork_ruby(); - pid = rb_fork(); - err = errno; - if (status) { - status->pid = pid; - status->error = err; + disable_child_handler_before_fork(&old); + { + pid = rb_fork(); + err = errno; + if (status) { + status->pid = pid; + status->error = err; + } } - after_fork_ruby(pid); disable_child_handler_fork_parent(&old); /* yes, bad name */ + after_fork_ruby(pid); if (pid >= 0) { /* fork succeed */ return pid; @@ -4663,11 +4663,16 @@ static VALUE do_spawn_process(VALUE arg) { struct spawn_args *argp = (struct spawn_args *)arg; + rb_execarg_parent_start1(argp->execarg); + return (VALUE)rb_spawn_process(DATA_PTR(argp->execarg), argp->errmsg.ptr, argp->errmsg.buflen); } +NOINLINE(static rb_pid_t + rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen)); + static rb_pid_t rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen) { @@ -4676,8 +4681,10 @@ rb_execarg_spawn(VALUE execarg_obj, char *errmsg, size_t errmsg_buflen) args.execarg = execarg_obj; args.errmsg.ptr = errmsg; args.errmsg.buflen = errmsg_buflen; - return (rb_pid_t)rb_ensure(do_spawn_process, (VALUE)&args, - execarg_parent_end, execarg_obj); + + rb_pid_t r = (rb_pid_t)rb_ensure(do_spawn_process, (VALUE)&args, + execarg_parent_end, execarg_obj); + return r; } static rb_pid_t @@ -4820,13 +4827,14 @@ rb_spawn(int argc, const VALUE *argv) static VALUE rb_f_system(int argc, VALUE *argv, VALUE _) { + rb_thread_t *th = GET_THREAD(); VALUE execarg_obj = rb_execarg_new(argc, argv, TRUE, TRUE); struct rb_execarg *eargp = rb_execarg_get(execarg_obj); struct rb_process_status status = {0}; eargp->status = &status; - rb_last_status_clear(); + last_status_clear(th); // This function can set the thread's last status. // May be different from waitpid_state.pid on exec failure. @@ -4834,12 +4842,10 @@ rb_f_system(int argc, VALUE *argv, VALUE _) if (pid > 0) { VALUE status = rb_process_status_wait(pid, 0); - struct rb_process_status *data = rb_check_typeddata(status, &rb_process_status_type); - // Set the last status: rb_obj_freeze(status); - GET_THREAD()->last_status = status; + th->last_status = status; if (data->status == EXIT_SUCCESS) { return Qtrue; |