diff options
Diffstat (limited to 'ractor.c')
| -rw-r--r-- | ractor.c | 528 |
1 files changed, 342 insertions, 186 deletions
@@ -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; |
