mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
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]
This commit is contained in:
parent
096ee0648e
commit
be1bbd5b7d
27 changed files with 3580 additions and 1524 deletions
66
process.c
66
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,24 +1660,11 @@ 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)
|
||||
{
|
||||
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();
|
||||
rb_thread_reset_timer_thread();
|
||||
rb_thread_start_timer_thread();
|
||||
}
|
||||
|
||||
#if defined HAVE_WORKING_FORK || defined HAVE_DAEMON
|
||||
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue