summaryrefslogtreecommitdiff
path: root/ractor.c
diff options
context:
space:
mode:
Diffstat (limited to 'ractor.c')
-rw-r--r--ractor.c528
1 files changed, 342 insertions, 186 deletions
diff --git a/ractor.c b/ractor.c
index c4d748e69c..f94c06cc73 100644
--- a/ractor.c
+++ b/ractor.c
@@ -3,9 +3,9 @@
#include "ruby/ruby.h"
#include "ruby/thread.h"
#include "ruby/ractor.h"
+#include "ruby/re.h"
#include "ruby/thread_native.h"
#include "vm_core.h"
-#include "eval_intern.h"
#include "vm_sync.h"
#include "ractor_core.h"
#include "internal/complex.h"
@@ -16,6 +16,7 @@
#include "internal/ractor.h"
#include "internal/rational.h"
#include "internal/struct.h"
+#include "internal/st.h"
#include "internal/thread.h"
#include "variable.h"
#include "yjit.h"
@@ -72,10 +73,17 @@ ractor_lock(rb_ractor_t *r, const char *file, int line)
ASSERT_ractor_unlocking(r);
rb_native_mutex_lock(&r->sync.lock);
+ const rb_execution_context_t *ec = rb_current_ec_noinline();
+ if (ec) {
+ rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
+ VM_ASSERT(!cr->malloc_gc_disabled);
+ cr->malloc_gc_disabled = true;
+ }
+
#if RACTOR_CHECK_MODE > 0
- if (rb_current_execution_context(false) != NULL) {
- rb_ractor_t *cr = rb_current_ractor_raw(false);
- r->sync.locked_by = cr ? rb_ractor_self(cr) : Qundef;
+ if (ec != NULL) {
+ rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
+ r->sync.locked_by = rb_ractor_self(cr);
}
#endif
@@ -99,6 +107,14 @@ ractor_unlock(rb_ractor_t *r, const char *file, int line)
#if RACTOR_CHECK_MODE > 0
r->sync.locked_by = Qnil;
#endif
+
+ const rb_execution_context_t *ec = rb_current_ec_noinline();
+ if (ec) {
+ rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
+ VM_ASSERT(cr->malloc_gc_disabled);
+ cr->malloc_gc_disabled = false;
+ }
+
rb_native_mutex_unlock(&r->sync.lock);
RUBY_DEBUG_LOG2(file, line, "r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
@@ -192,30 +208,73 @@ static void ractor_sync_free(rb_ractor_t *r);
static size_t ractor_sync_memsize(const rb_ractor_t *r);
static void ractor_sync_init(rb_ractor_t *r);
+static int
+mark_targeted_hook_list(st_data_t key, st_data_t value, st_data_t _arg)
+{
+ rb_hook_list_t *hook_list = (rb_hook_list_t*)value;
+
+ if (hook_list->type == hook_list_type_targeted_iseq) {
+ rb_gc_mark((VALUE)key);
+ }
+ else {
+ rb_method_definition_t *def = (rb_method_definition_t*)key;
+ RUBY_ASSERT(hook_list->type == hook_list_type_targeted_def);
+ rb_gc_mark(def->body.bmethod.proc);
+ }
+ rb_hook_list_mark(hook_list);
+
+ return ST_CONTINUE;
+}
+
static void
ractor_mark(void *ptr)
{
rb_ractor_t *r = (rb_ractor_t *)ptr;
-
- // mark received messages
- ractor_sync_mark(r);
+ bool checking_shareable = rb_gc_checking_shareable();
rb_gc_mark(r->loc);
rb_gc_mark(r->name);
- rb_gc_mark(r->r_stdin);
- rb_gc_mark(r->r_stdout);
- rb_gc_mark(r->r_stderr);
- rb_hook_list_mark(&r->pub.hooks);
-
- if (r->threads.cnt > 0) {
- rb_thread_t *th = 0;
- ccan_list_for_each(&r->threads.set, th, lt_node) {
- VM_ASSERT(th != NULL);
- rb_gc_mark(th->self);
+
+ if (!checking_shareable) {
+ // may unshareable objects
+ rb_gc_mark(r->r_stdin);
+ rb_gc_mark(r->r_stdout);
+ rb_gc_mark(r->r_stderr);
+ rb_gc_mark(r->verbose);
+ rb_gc_mark(r->debug);
+
+ // mark received messages
+ ractor_sync_mark(r);
+
+ rb_hook_list_mark(&r->pub.hooks);
+ if (r->pub.targeted_hooks.num_entries) {
+ st_foreach(&r->pub.targeted_hooks, mark_targeted_hook_list, 0);
+ }
+
+ if (r->threads.cnt > 0) {
+ rb_thread_t *th = 0;
+ ccan_list_for_each(&r->threads.set, th, lt_node) {
+ VM_ASSERT(th != NULL);
+ rb_gc_mark(th->self);
+ }
}
+
+ ractor_local_storage_mark(r);
}
+}
- ractor_local_storage_mark(r);
+static int
+free_targeted_hook_lists(st_data_t key, st_data_t val, st_data_t _arg)
+{
+ rb_hook_list_t *hook_list = (rb_hook_list_t*)val;
+ rb_hook_list_free(hook_list);
+ return ST_DELETE;
+}
+
+static void
+free_targeted_hooks(st_table *hooks_tbl)
+{
+ st_foreach(hooks_tbl, free_targeted_hook_lists, 0);
}
static void
@@ -223,12 +282,14 @@ ractor_free(void *ptr)
{
rb_ractor_t *r = (rb_ractor_t *)ptr;
RUBY_DEBUG_LOG("free r:%d", rb_ractor_id(r));
+ free_targeted_hooks(&r->pub.targeted_hooks);
rb_native_mutex_destroy(&r->sync.lock);
#ifdef RUBY_THREAD_WIN32_H
rb_native_cond_destroy(&r->sync.wakeup_cond);
#endif
ractor_local_storage_free(r);
rb_hook_list_free(&r->pub.hooks);
+ rb_st_free_embedded_table(&r->pub.targeted_hooks);
if (r->newobj_cache) {
RUBY_ASSERT(r == ruby_single_main_ractor);
@@ -238,7 +299,9 @@ ractor_free(void *ptr)
}
ractor_sync_free(r);
- ruby_xfree(r);
+ if (!r->main_ractor) {
+ SIZED_FREE(r);
+ }
}
static size_t
@@ -280,7 +343,8 @@ RACTOR_PTR(VALUE self)
return r;
}
-static rb_atomic_t ractor_last_id;
+#define MAIN_RACTOR_ID 1
+static rb_atomic_t ractor_last_id = MAIN_RACTOR_ID;
#if RACTOR_CHECK_MODE > 0
uint32_t
@@ -399,22 +463,24 @@ ractor_alloc(VALUE klass)
VALUE rv = TypedData_Make_Struct(klass, rb_ractor_t, &ractor_data_type, r);
FL_SET_RAW(rv, RUBY_FL_SHAREABLE);
r->pub.self = rv;
+ r->next_ec_serial = 1;
VM_ASSERT(ractor_status_p(r, ractor_created));
return rv;
}
+static rb_ractor_t _main_ractor = {
+ .loc = Qnil,
+ .name = Qnil,
+ .pub.id = MAIN_RACTOR_ID,
+ .pub.self = Qnil,
+ .next_ec_serial = 1,
+ .main_ractor = true,
+};
+
rb_ractor_t *
rb_ractor_main_alloc(void)
{
- rb_ractor_t *r = ruby_mimcalloc(1, sizeof(rb_ractor_t));
- if (r == NULL) {
- fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
- exit(EXIT_FAILURE);
- }
- r->pub.id = ++ractor_last_id;
- r->loc = Qnil;
- r->name = Qnil;
- r->pub.self = Qnil;
+ rb_ractor_t *r = &_main_ractor;
r->newobj_cache = rb_gc_ractor_cache_alloc(r);
ruby_single_main_ractor = r;
@@ -464,6 +530,8 @@ static void
ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
{
ractor_sync_init(r);
+ st_init_existing_numtable_with_size(&r->pub.targeted_hooks, 0);
+ r->pub.hooks.type = hook_list_type_ractor_local;
// thread management
rb_thread_sched_init(&r->threads.sched, false);
@@ -478,10 +546,12 @@ ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
rb_enc_name(enc));
}
- name = rb_str_new_frozen(name);
+ name = RB_OBJ_SET_SHAREABLE(rb_str_new_frozen(name));
}
- r->name = name;
+
+ if (!SPECIAL_CONST_P(loc)) RB_OBJ_SET_SHAREABLE(loc);
r->loc = loc;
+ r->name = name;
}
void
@@ -503,7 +573,6 @@ ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VAL
rb_ractor_t *r = RACTOR_PTR(rv);
ractor_init(r, name, loc);
- // can block here
r->pub.id = ractor_next_id();
RUBY_DEBUG_LOG("r:%u", r->pub.id);
@@ -812,13 +881,11 @@ rb_ractor_terminate_all(void)
VM_ASSERT(cr == GET_RACTOR()); // only main-ractor's main-thread should kick it.
- if (vm->ractor.cnt > 1) {
- RB_VM_LOCK();
- {
- ractor_terminal_interrupt_all(vm); // kill all ractors
- }
- RB_VM_UNLOCK();
+ RB_VM_LOCK();
+ {
+ ractor_terminal_interrupt_all(vm); // kill all ractors
}
+ RB_VM_UNLOCK();
rb_thread_terminate_all(GET_THREAD()); // kill other threads in main-ractor and wait
RB_VM_LOCK();
@@ -831,6 +898,17 @@ rb_ractor_terminate_all(void)
rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
rb_del_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 /* ms */);
+#ifdef RUBY_THREAD_PTHREAD_H
+ while (vm->ractor.sched.barrier_waiting) {
+ // A barrier is waiting. Threads relinquish the VM lock before joining the barrier and
+ // since we just acquired the VM lock back, we're blocking other threads from joining it.
+ // We loop until the barrier is over. We can't join this barrier because our thread isn't added to
+ // running_threads until the call below to `rb_add_running_thread`.
+ RB_VM_UNLOCK();
+ unsigned int lev;
+ RB_VM_LOCK_ENTER_LEV_NB(&lev);
+ }
+#endif
rb_add_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
@@ -879,36 +957,16 @@ ractor_moved_missing(int argc, VALUE *argv, VALUE self)
*
* Raised when an attempt is made to send a message to a closed port,
* or to retrieve a message from a closed and empty port.
- * Ports may be closed explicitly with Ractor#close_outgoing/close_incoming
+ * Ports may be closed explicitly with Ractor::Port#close
* and are closed implicitly when a Ractor terminates.
*
- * r = Ractor.new { sleep(500) }
- * r.close_outgoing
- * r.take # Ractor::ClosedError
- *
- * ClosedError is a descendant of StopIteration, so the closing of the ractor will break
- * the loops without propagating the error:
+ * port = Ractor::Port.new
+ * port.close
+ * port << "test" # Ractor::ClosedError
+ * port.receive # Ractor::ClosedError
*
- * r = Ractor.new do
- * loop do
- * msg = receive # raises ClosedError and loop traps it
- * puts "Received: #{msg}"
- * end
- * puts "loop exited"
- * end
- *
- * 3.times{|i| r << i}
- * r.close_incoming
- * r.take
- * puts "Continue successfully"
- *
- * This will print:
- *
- * Received: 0
- * Received: 1
- * Received: 2
- * loop exited
- * Continue successfully
+ * ClosedError is a descendant of StopIteration, so the closing of a port will break
+ * out of loops without propagating the error.
*/
/*
@@ -921,14 +979,14 @@ ractor_moved_missing(int argc, VALUE *argv, VALUE self)
/*
* Document-class: Ractor::RemoteError
*
- * Raised on attempt to Ractor#take if there was an uncaught exception in the Ractor.
+ * Raised on Ractor#join or Ractor#value if there was an uncaught exception in the Ractor.
* Its +cause+ will contain the original exception, and +ractor+ is the original ractor
* it was raised in.
*
* r = Ractor.new { raise "Something weird happened" }
*
* begin
- * r.take
+ * r.value
* rescue => e
* p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
* p e.ractor == r # => true
@@ -940,7 +998,7 @@ ractor_moved_missing(int argc, VALUE *argv, VALUE self)
/*
* Document-class: Ractor::MovedError
*
- * Raised on an attempt to access an object which was moved in Ractor#send or Ractor.yield.
+ * Raised on an attempt to access an object which was moved in Ractor#send or Ractor::Port#send.
*
* r = Ractor.new { sleep }
*
@@ -955,7 +1013,7 @@ ractor_moved_missing(int argc, VALUE *argv, VALUE self)
* Document-class: Ractor::MovedObject
*
* A special object which replaces any value that was moved to another ractor in Ractor#send
- * or Ractor.yield. Any attempt to access the object results in Ractor::MovedError.
+ * or Ractor::Port#send. Any attempt to access the object results in Ractor::MovedError.
*
* r = Ractor.new { receive }
*
@@ -1101,6 +1159,50 @@ rb_ractor_hooks(rb_ractor_t *cr)
return &cr->pub.hooks;
}
+st_table *
+rb_ractor_targeted_hooks(rb_ractor_t *cr)
+{
+ return &cr->pub.targeted_hooks;
+}
+
+static void
+rb_obj_set_shareable_no_assert(VALUE obj)
+{
+ FL_SET_RAW(obj, FL_SHAREABLE);
+
+ if (rb_obj_gen_fields_p(obj)) {
+ VALUE fields = rb_obj_fields_no_ractor_check(obj);
+ if (imemo_type_p(fields, imemo_fields)) {
+ // no recursive mark
+ FL_SET_RAW(fields, FL_SHAREABLE);
+ }
+ }
+}
+
+#ifndef STRICT_VERIFY_SHAREABLE
+#define STRICT_VERIFY_SHAREABLE 0
+#endif
+
+bool
+rb_ractor_verify_shareable(VALUE obj)
+{
+#if STRICT_VERIFY_SHAREABLE
+ rb_gc_verify_shareable(obj);
+#endif
+ return true;
+}
+
+VALUE
+rb_obj_set_shareable(VALUE obj)
+{
+ RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
+
+ rb_obj_set_shareable_no_assert(obj);
+ RUBY_ASSERT(rb_ractor_verify_shareable(obj));
+
+ return obj;
+}
+
/// traverse function
// 2: stop search
@@ -1168,6 +1270,7 @@ obj_traverse_rec(struct obj_traverse_data *data)
{
if (UNLIKELY(!data->rec)) {
data->rec_hash = rb_ident_hash_new();
+ rb_obj_hide(data->rec_hash);
data->rec = RHASH_ST_TABLE(data->rec_hash);
}
return data->rec;
@@ -1218,7 +1321,6 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
case T_REGEXP:
case T_FILE:
case T_SYMBOL:
- case T_MATCH:
break;
case T_OBJECT:
@@ -1227,6 +1329,8 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
case T_ARRAY:
{
+ rb_ary_cancel_sharing(obj);
+
for (int i = 0; i < RARRAY_LENINT(obj); i++) {
VALUE e = rb_ary_entry(obj, i);
if (obj_traverse_i(e, data)) return 1;
@@ -1249,7 +1353,7 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
case T_STRUCT:
{
- long len = RSTRUCT_LEN(obj);
+ long len = RSTRUCT_LEN_RAW(obj);
const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
for (long i=0; i<len; i++) {
@@ -1258,6 +1362,10 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
}
break;
+ case T_MATCH:
+ if (obj_traverse_i(RMATCH(obj)->str, data)) return 1;
+ break;
+
case T_RATIONAL:
if (obj_traverse_i(RRATIONAL(obj)->num, data)) return 1;
if (obj_traverse_i(RRATIONAL(obj)->den, data)) return 1;
@@ -1343,7 +1451,7 @@ allow_frozen_shareable_p(VALUE obj)
if (!RB_TYPE_P(obj, T_DATA)) {
return true;
}
- else if (RTYPEDDATA_P(obj)) {
+ else {
const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
return true;
@@ -1354,6 +1462,26 @@ allow_frozen_shareable_p(VALUE obj)
}
static enum obj_traverse_iterator_result
+make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_result result)
+{
+ if (!RB_OBJ_FROZEN_RAW(obj)) {
+ rb_funcall(obj, idFreeze, 0);
+
+ if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
+ rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
+ }
+
+ if (RB_OBJ_SHAREABLE_P(obj)) {
+ return traverse_skip;
+ }
+ }
+
+ return result;
+}
+
+static int obj_refer_only_shareables_p(VALUE obj);
+
+static enum obj_traverse_iterator_result
make_shareable_check_shareable(VALUE obj)
{
VM_ASSERT(!SPECIAL_CONST_P(obj));
@@ -1362,8 +1490,22 @@ make_shareable_check_shareable(VALUE obj)
return traverse_skip;
}
else if (!allow_frozen_shareable_p(obj)) {
- if (rb_obj_is_proc(obj)) {
- rb_proc_ractor_make_shareable(obj);
+ VM_ASSERT(RB_TYPE_P(obj, T_DATA));
+ const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
+
+ if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC) {
+ if (obj_refer_only_shareables_p(obj)) {
+ make_shareable_check_shareable_freeze(obj, traverse_skip);
+ RB_OBJ_SET_SHAREABLE(obj);
+ return traverse_skip;
+ }
+ else {
+ rb_raise(rb_eRactorError,
+ "can not make shareable object for %+"PRIsVALUE" because it refers unshareable objects", obj);
+ }
+ }
+ else if (rb_obj_is_proc(obj)) {
+ rb_proc_ractor_make_shareable(obj, Qundef);
return traverse_cont;
}
else {
@@ -1392,25 +1534,17 @@ make_shareable_check_shareable(VALUE obj)
break;
}
- if (!RB_OBJ_FROZEN_RAW(obj)) {
- rb_funcall(obj, idFreeze, 0);
-
- if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
- rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
- }
-
- if (RB_OBJ_SHAREABLE_P(obj)) {
- return traverse_skip;
- }
- }
-
- return traverse_cont;
+ return make_shareable_check_shareable_freeze(obj, traverse_cont);
}
static enum obj_traverse_iterator_result
mark_shareable(VALUE obj)
{
- FL_SET_RAW(obj, RUBY_FL_SHAREABLE);
+ if (RB_TYPE_P(obj, T_STRING)) {
+ rb_str_make_independent(obj);
+ }
+
+ rb_obj_set_shareable_no_assert(obj);
return traverse_cont;
}
@@ -1672,10 +1806,10 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
else if (data->replacement != _val) { RB_OBJ_WRITE(parent_obj, &v, data->replacement); } \
} while (0)
- if (UNLIKELY(rb_obj_exivar_p(obj))) {
+ if (UNLIKELY(rb_obj_gen_fields_p(obj))) {
VALUE fields_obj = rb_obj_fields_no_ractor_check(obj);
- if (UNLIKELY(rb_shape_obj_too_complex_p(obj))) {
+ if (UNLIKELY(rb_obj_shape_complex_p(obj))) {
struct obj_traverse_replace_callback_data d = {
.stop = false,
.data = data,
@@ -1705,7 +1839,6 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
case T_REGEXP:
case T_FILE:
case T_SYMBOL:
- case T_MATCH:
break;
case T_STRING:
rb_str_make_independent(obj);
@@ -1713,7 +1846,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
case T_OBJECT:
{
- if (rb_shape_obj_too_complex_p(obj)) {
+ if (rb_obj_shape_complex_p(obj)) {
struct obj_traverse_replace_callback_data d = {
.stop = false,
.data = data,
@@ -1728,7 +1861,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
if (d.stop) return 1;
}
else {
- uint32_t len = ROBJECT_FIELDS_COUNT(obj);
+ uint32_t len = ROBJECT_FIELDS_COUNT_NOT_COMPLEX(obj);
VALUE *ptr = ROBJECT_FIELDS(obj);
for (uint32_t i = 0; i < len; i++) {
@@ -1781,7 +1914,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
case T_STRUCT:
{
- long len = RSTRUCT_LEN(obj);
+ long len = RSTRUCT_LEN_RAW(obj);
const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
for (long i=0; i<len; i++) {
@@ -1790,6 +1923,10 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
}
break;
+ case T_MATCH:
+ CHECK_AND_REPLACE(obj, RMATCH(obj)->str);
+ break;
+
case T_RATIONAL:
CHECK_AND_REPLACE(obj, RRATIONAL(obj)->num);
CHECK_AND_REPLACE(obj, RRATIONAL(obj)->den);
@@ -1856,16 +1993,16 @@ rb_obj_traverse_replace(VALUE obj,
}
static const bool wb_protected_types[RUBY_T_MASK] = {
- [T_OBJECT] = RGENGC_WB_PROTECTED_OBJECT,
- [T_HASH] = RGENGC_WB_PROTECTED_HASH,
- [T_ARRAY] = RGENGC_WB_PROTECTED_ARRAY,
- [T_STRING] = RGENGC_WB_PROTECTED_STRING,
- [T_STRUCT] = RGENGC_WB_PROTECTED_STRUCT,
- [T_COMPLEX] = RGENGC_WB_PROTECTED_COMPLEX,
- [T_REGEXP] = RGENGC_WB_PROTECTED_REGEXP,
- [T_MATCH] = RGENGC_WB_PROTECTED_MATCH,
- [T_FLOAT] = RGENGC_WB_PROTECTED_FLOAT,
- [T_RATIONAL] = RGENGC_WB_PROTECTED_RATIONAL,
+ [T_OBJECT] = true,
+ [T_HASH] = true,
+ [T_ARRAY] = true,
+ [T_STRING] = true,
+ [T_STRUCT] = true,
+ [T_COMPLEX] = true,
+ [T_REGEXP] = true,
+ [T_MATCH] = true,
+ [T_FLOAT] = true,
+ [T_RATIONAL] = true,
};
static enum obj_traverse_iterator_result
@@ -1877,8 +2014,9 @@ move_enter(VALUE obj, struct obj_traverse_replace_data *data)
}
else {
VALUE type = RB_BUILTIN_TYPE(obj);
- type |= wb_protected_types[type] ? FL_WB_PROTECTED : 0;
- NEWOBJ_OF(moved, struct RBasic, 0, type, rb_gc_obj_slot_size(obj), 0);
+ size_t slot_size = rb_gc_obj_slot_size(obj);
+ VALUE moved = rb_newobj(GET_EC(), 0, type, RBASIC_SHAPE_ID(obj), wb_protected_types[type], slot_size);
+ MEMZERO(((struct RBasic *)moved) + 1, char, slot_size - sizeof(struct RBasic));
data->replacement = (VALUE)moved;
return traverse_cont;
}
@@ -1901,14 +2039,13 @@ move_leave(VALUE obj, struct obj_traverse_replace_data *data)
rb_gc_writebarrier_remember(data->replacement);
void rb_replace_generic_ivar(VALUE clone, VALUE obj); // variable.c
-
- rb_gc_obj_id_moved(data->replacement);
-
- if (UNLIKELY(rb_obj_exivar_p(obj))) {
+ if (UNLIKELY(rb_obj_gen_fields_p(obj))) {
rb_replace_generic_ivar(data->replacement, obj);
}
- VALUE flags = T_OBJECT | FL_FREEZE | ROBJECT_EMBED | (RBASIC(obj)->flags & FL_PROMOTED);
+ rb_gc_obj_id_moved(data->replacement);
+
+ VALUE flags = T_OBJECT | FL_FREEZE | (RBASIC(obj)->flags & FL_PROMOTED);
// Avoid mutations using bind_call, etc.
MEMZERO((char *)obj, char, sizeof(struct RBasic));
@@ -1929,6 +2066,31 @@ ractor_move(VALUE obj)
}
}
+static VALUE
+ractor_call_clone_try(VALUE obj)
+{
+ return rb_funcall(obj, idClone, 0);
+}
+
+static VALUE
+ractor_call_clone_rescue(VALUE obj, VALUE exc)
+{
+ rb_raise(rb_eRactorError, "can't clone unshareable instance of %"PRIsVALUE, rb_class_of(obj));
+ UNREACHABLE_RETURN(Qnil);
+}
+
+static VALUE
+ractor_obj_clone(VALUE obj)
+{
+ VALUE clone = rb_rescue(ractor_call_clone_try, obj, ractor_call_clone_rescue, obj);
+
+ if (obj == clone) {
+ rb_raise(rb_eRactorError, "#clone returned self");
+ }
+
+ return clone;
+}
+
static enum obj_traverse_iterator_result
copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
{
@@ -1937,7 +2099,7 @@ copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
return traverse_skip;
}
else {
- data->replacement = rb_obj_clone(obj);
+ data->replacement = ractor_obj_clone(obj);
return traverse_cont;
}
}
@@ -2074,7 +2236,7 @@ rb_ractor_local_storage_delkey(rb_ractor_local_key_t key)
RB_VM_LOCKING() {
if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
- REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa);
+ SIZED_REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa, freed_ractor_local_keys.cnt);
}
freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
}
@@ -2173,12 +2335,12 @@ void
rb_ractor_finish_marking(void)
{
for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
- ruby_xfree(freed_ractor_local_keys.keys[i]);
+ SIZED_FREE(freed_ractor_local_keys.keys[i]);
}
freed_ractor_local_keys.cnt = 0;
if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
- REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA);
+ SIZED_REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA, freed_ractor_local_keys.capa);
}
}
@@ -2262,29 +2424,43 @@ ractor_local_value_store_if_absent(rb_execution_context_t *ec, VALUE self, VALUE
return rb_mutex_synchronize(cr->local_storage_store_lock, ractor_local_value_store_i, (VALUE)&data);
}
+// shareable_proc
+
+static VALUE
+ractor_shareable_proc(rb_execution_context_t *ec, VALUE replace_self, bool is_lambda)
+{
+ if (!rb_ractor_shareable_p(replace_self)) {
+ rb_raise(rb_eRactorIsolationError, "self should be shareable: %" PRIsVALUE, replace_self);
+ }
+ else {
+ VALUE proc = is_lambda ? rb_block_lambda() : rb_block_proc();
+ return rb_proc_ractor_make_shareable(rb_proc_dup(proc), replace_self);
+ }
+}
+
// Ractor#require
struct cross_ractor_require {
VALUE port;
- VALUE result;
- VALUE exception;
+ bool raised;
- // require
- VALUE feature;
+ union {
+ struct {
+ VALUE feature;
+ } require;
- // autoload
- VALUE module;
- ID name;
+ struct {
+ VALUE module;
+ ID name;
+ } autoload;
+ } as;
bool silent;
};
RUBY_REFERENCES(cross_ractor_require_refs) = {
RUBY_REF_EDGE(struct cross_ractor_require, port),
- RUBY_REF_EDGE(struct cross_ractor_require, result),
- RUBY_REF_EDGE(struct cross_ractor_require, exception),
- RUBY_REF_EDGE(struct cross_ractor_require, feature),
- RUBY_REF_EDGE(struct cross_ractor_require, module),
+ RUBY_REF_EDGE(struct cross_ractor_require, as.require.feature),
RUBY_REF_END
};
@@ -2304,20 +2480,18 @@ require_body(VALUE crr_obj)
{
struct cross_ractor_require *crr;
TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
+ VALUE feature = crr->as.require.feature;
ID require;
CONST_ID(require, "require");
if (crr->silent) {
int rb_require_internal_silent(VALUE fname);
-
- RB_OBJ_WRITE(crr_obj, &crr->result, INT2NUM(rb_require_internal_silent(crr->feature)));
+ return INT2NUM(rb_require_internal_silent(feature));
}
else {
- RB_OBJ_WRITE(crr_obj, &crr->result, rb_funcallv(Qnil, require, 1, &crr->feature));
+ return rb_funcallv(Qnil, require, 1, &feature);
}
-
- return Qnil;
}
static VALUE
@@ -2325,38 +2499,27 @@ require_rescue(VALUE crr_obj, VALUE errinfo)
{
struct cross_ractor_require *crr;
TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
-
- RB_OBJ_WRITE(crr_obj, &crr->exception, errinfo);
-
- return Qundef;
+ crr->raised = true;
+ return errinfo;
}
static VALUE
-require_result_copy_body(VALUE crr_obj)
+require_result_send_body(VALUE ary)
{
- struct cross_ractor_require *crr;
- TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
+ VALUE port = RARRAY_AREF(ary, 0);
+ VALUE results = RARRAY_AREF(ary, 1);
- if (crr->exception != Qundef) {
- VM_ASSERT(crr->result == Qundef);
- RB_OBJ_WRITE(crr_obj, &crr->exception, ractor_copy(crr->exception));
- }
- else{
- VM_ASSERT(crr->result != Qundef);
- RB_OBJ_WRITE(crr_obj, &crr->result, ractor_copy(crr->result));
- }
+ rb_execution_context_t *ec = GET_EC();
+ ractor_port_send(ec, port, results, Qfalse);
return Qnil;
}
static VALUE
-require_result_copy_resuce(VALUE crr_obj, VALUE errinfo)
+require_result_send_resuce(VALUE port, VALUE errinfo)
{
- struct cross_ractor_require *crr;
- TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
-
- RB_OBJ_WRITE(crr_obj, &crr->exception, errinfo);
-
+ // TODO: need rescue?
+ ractor_port_send(GET_EC(), port, errinfo, Qfalse);
return Qnil;
}
@@ -2367,25 +2530,26 @@ ractor_require_protect(VALUE crr_obj, VALUE (*func)(VALUE))
TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
const bool silent = crr->silent;
+
VALUE debug, errinfo;
if (silent) {
debug = ruby_debug;
errinfo = rb_errinfo();
}
- // catch any error
- rb_rescue2(func, crr_obj,
- require_rescue, crr_obj, rb_eException, 0);
+ // get normal result or raised exception (with crr->raised == true)
+ VALUE result = rb_rescue2(func, crr_obj, require_rescue, crr_obj, rb_eException, 0);
if (silent) {
ruby_debug = debug;
rb_set_errinfo(errinfo);
}
- rb_rescue2(require_result_copy_body, crr_obj,
- require_result_copy_resuce, crr_obj, rb_eException, 0);
+ rb_rescue2(require_result_send_body,
+ // [port, [result, raised]]
+ rb_ary_new_from_args(2, crr->port, rb_ary_new_from_args(2, result, crr->raised ? Qtrue : Qfalse)),
+ require_result_send_resuce, rb_eException, crr->port);
- ractor_port_send(GET_EC(), crr->port, Qtrue, Qfalse);
RB_GC_GUARD(crr_obj);
return Qnil;
}
@@ -2404,13 +2568,12 @@ rb_ractor_require(VALUE feature, bool silent)
struct cross_ractor_require *crr;
VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
- FL_SET_RAW(crr_obj, RUBY_FL_SHAREABLE);
+ RB_OBJ_SET_SHAREABLE(crr_obj); // TODO: internal data?
// Convert feature to proper file path and make it shareable as fstring
- RB_OBJ_WRITE(crr_obj, &crr->feature, rb_fstring(FilePathValue(feature)));
- RB_OBJ_WRITE(crr_obj, &crr->port, ractor_port_new(GET_RACTOR()));
- crr->result = Qundef;
- crr->exception = Qundef;
+ RB_OBJ_WRITE(crr_obj, &crr->as.require.feature, rb_fstring(FilePathValue(feature)));
+ RB_OBJ_WRITE(crr_obj, &crr->port, rb_ractor_make_shareable(ractor_port_new(GET_RACTOR())));
+ crr->raised = false;
crr->silent = silent;
rb_execution_context_t *ec = GET_EC();
@@ -2418,20 +2581,17 @@ rb_ractor_require(VALUE feature, bool silent)
rb_ractor_interrupt_exec(main_r, ractor_require_func, (void *)crr_obj, rb_interrupt_exec_flag_value_data);
// wait for require done
- ractor_port_receive(ec, crr->port);
+ VALUE results = ractor_port_receive(ec, crr->port);
ractor_port_close(ec, crr->port);
- VALUE exc = crr->exception;
- VALUE result = crr->result;
+ VALUE exc = rb_ary_pop(results);
+ VALUE result = rb_ary_pop(results);
RB_GC_GUARD(crr_obj);
- if (exc != Qundef) {
- ractor_reset_belonging(exc);
- rb_exc_raise(exc);
+ if (RTEST(exc)) {
+ rb_exc_raise(result);
}
else {
- RUBY_ASSERT(result != Qundef);
- ractor_reset_belonging(result);
return result;
}
}
@@ -2447,10 +2607,7 @@ autoload_load_body(VALUE crr_obj)
{
struct cross_ractor_require *crr;
TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
-
- RB_OBJ_WRITE(crr_obj, &crr->result, rb_autoload_load(crr->module, crr->name));
-
- return Qnil;
+ return rb_autoload_load(crr->as.autoload.module, crr->as.autoload.name);
}
static VALUE
@@ -2464,27 +2621,26 @@ rb_ractor_autoload_load(VALUE module, ID name)
{
struct cross_ractor_require *crr;
VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
- FL_SET_RAW(crr_obj, RUBY_FL_SHAREABLE);
- RB_OBJ_WRITE(crr_obj, &crr->module, module);
- RB_OBJ_WRITE(crr_obj, &crr->name, name);
- RB_OBJ_WRITE(crr_obj, &crr->port, ractor_port_new(GET_RACTOR()));
- crr->result = Qundef;
- crr->exception = Qundef;
+ RB_OBJ_SET_SHAREABLE(crr_obj); // TODO: internal data?
+
+ RB_OBJ_WRITE(crr_obj, &crr->as.autoload.module, module);
+ RB_OBJ_WRITE(crr_obj, &crr->as.autoload.name, name);
+ RB_OBJ_WRITE(crr_obj, &crr->port, rb_ractor_make_shareable(ractor_port_new(GET_RACTOR())));
rb_execution_context_t *ec = GET_EC();
rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
rb_ractor_interrupt_exec(main_r, ractor_autoload_load_func, (void *)crr_obj, rb_interrupt_exec_flag_value_data);
// wait for require done
- ractor_port_receive(ec, crr->port);
+ VALUE results = ractor_port_receive(ec, crr->port);
ractor_port_close(ec, crr->port);
- VALUE exc = crr->exception;
- VALUE result = crr->result;
+ VALUE exc = rb_ary_pop(results);
+ VALUE result = rb_ary_pop(results);
RB_GC_GUARD(crr_obj);
- if (exc != Qundef) {
- rb_exc_raise(exc);
+ if (RTEST(exc)) {
+ rb_exc_raise(result);
}
else {
return result;