Skip to content

Commit

Permalink
Introduce Ractor mechanism for parallel execution
Browse files Browse the repository at this point in the history
This commit introduces Ractor mechanism to run Ruby program in
parallel. See doc/ractor.md for more details about Ractor.
See ticket [Feature #17100] to see the implementation details
and discussions.

[Feature #17100]

This commit does not complete the implementation. You can find
many bugs on using Ractor. Also the specification will be changed
so that this feature is experimental. You will see a warning when
you make the first Ractor with `Ractor.new`.

I hope this feature can help programmers from thread-safety issues.
  • Loading branch information
ko1 committed Sep 3, 2020
1 parent eeb5325 commit 79df14c
Show file tree
Hide file tree
Showing 41 changed files with 5,952 additions and 784 deletions.
516 changes: 516 additions & 0 deletions bootstraptest/test_ractor.rb

Large diffs are not rendered by default.

447 changes: 446 additions & 1 deletion common.mk

Large diffs are not rendered by default.

17 changes: 16 additions & 1 deletion compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,16 @@ iseq_block_param_id_p(const rb_iseq_t *iseq, ID id, int *pidx, int *plevel)
}
}

static void
check_access_outer_variables(const rb_iseq_t *iseq, int level)
{
// set access_outer_variables
for (int i=0; i<level; i++) {
iseq->body->access_outer_variables = TRUE;
iseq = iseq->body->parent_iseq;
}
}

static void
iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level)
{
Expand All @@ -1609,6 +1619,7 @@ iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, in
else {
ADD_INSN2(seq, line, getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
}
check_access_outer_variables(iseq, level);
}

static void
Expand All @@ -1620,6 +1631,7 @@ iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, in
else {
ADD_INSN2(seq, line, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
}
check_access_outer_variables(iseq, level);
}


Expand Down Expand Up @@ -8222,6 +8234,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
if (popped) {
ADD_INSN(ret, line, pop);
}

iseq->body->access_outer_variables = TRUE;
break;
}
case NODE_LVAR:{
Expand Down Expand Up @@ -8680,7 +8694,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
VALUE flag = INT2FIX(excl);
const NODE *b = node->nd_beg;
const NODE *e = node->nd_end;
if (optimizable_range_item_p(b) && optimizable_range_item_p(e)) {
// TODO: Ractor can not use cached Range objects
if (0 && optimizable_range_item_p(b) && optimizable_range_item_p(e)) {
if (!popped) {
VALUE bv = nd_type(b) == NODE_LIT ? b->nd_lit : Qnil;
VALUE ev = nd_type(e) == NODE_LIT ? e->nd_lit : Qnil;
Expand Down
18 changes: 11 additions & 7 deletions cont.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "mjit.h"
#include "vm_core.h"
#include "id_table.h"
#include "ractor.h"

static const int DEBUG = 0;

Expand Down Expand Up @@ -808,14 +809,15 @@ static inline void
ec_switch(rb_thread_t *th, rb_fiber_t *fiber)
{
rb_execution_context_t *ec = &fiber->cont.saved_ec;

ruby_current_execution_context_ptr = th->ec = ec;
rb_ractor_set_current_ec(th->ractor, th->ec = ec);
// ruby_current_execution_context_ptr = th->ec = ec;

/*
* timer-thread may set trap interrupt on previous th->ec at any time;
* ensure we do not delay (or lose) the trap interrupt handling.
*/
if (th->vm->main_thread == th && rb_signal_buff_size() > 0) {
if (th->vm->ractor.main_thread == th &&
rb_signal_buff_size() > 0) {
RUBY_VM_SET_TRAP_INTERRUPT(ec);
}

Expand Down Expand Up @@ -1873,7 +1875,7 @@ rb_fiber_start(void)
enum ruby_tag_type state;
int need_interrupt = TRUE;

VM_ASSERT(th->ec == ruby_current_execution_context_ptr);
VM_ASSERT(th->ec == GET_EC());
VM_ASSERT(FIBER_RESUMED_P(fiber));

if (fiber->blocking) {
Expand Down Expand Up @@ -1964,13 +1966,15 @@ rb_threadptr_root_fiber_release(rb_thread_t *th)
/* ignore. A root fiber object will free th->ec */
}
else {
rb_execution_context_t *ec = GET_EC();

VM_ASSERT(th->ec->fiber_ptr->cont.type == FIBER_CONTEXT);
VM_ASSERT(th->ec->fiber_ptr->cont.self == 0);
fiber_free(th->ec->fiber_ptr);

if (th->ec == ruby_current_execution_context_ptr) {
ruby_current_execution_context_ptr = NULL;
if (th->ec == ec) {
rb_ractor_set_current_ec(th->ractor, NULL);
}
fiber_free(th->ec->fiber_ptr);
th->ec = NULL;
}
}
Expand Down
3 changes: 1 addition & 2 deletions debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "vm_debug.h"
#include "vm_callinfo.h"
#include "ruby/thread_native.h"
#include "ractor.h"

/* This is the only place struct RIMemo is actually used */
struct RIMemo {
Expand Down Expand Up @@ -422,7 +423,6 @@ ruby_debug_log(const char *file, int line, const char *func_name, const char *fm
len += r;
}

#if 0 // not yet
// ractor information
if (GET_VM()->ractor.cnt > 1) {
rb_ractor_t *cr = GET_RACTOR();
Expand All @@ -433,7 +433,6 @@ ruby_debug_log(const char *file, int line, const char *func_name, const char *fm
len += r;
}
}
#endif

// thread information
if (!rb_thread_alone()) {
Expand Down
Loading

0 comments on commit 79df14c

Please sign in to comment.