aboutsummaryrefslogtreecommitdiffstats
path: root/process.c
diff options
context:
space:
mode:
authorKoichi Sasada <ko1@atdot.net>2023-04-10 10:53:13 +0900
committerKoichi Sasada <ko1@atdot.net>2023-10-12 14:47:01 +0900
commitbe1bbd5b7d40ad863ab35097765d3754726bbd54 (patch)
tree2995a0859bea1d6b2903dcd324f41869dbef14a1 /process.c
parent096ee0648e215915a3019c2cd68ba220d94eca12 (diff)
downloadruby-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.c64
1 files changed, 35 insertions, 29 deletions
diff --git a/process.c b/process.c
index 85f1fca467..c215c1f70d 100644
--- a/process.c
+++ b/process.c
@@ -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;