diff options
author | Koichi Sasada <ko1@atdot.net> | 2020-10-21 00:54:03 +0900 |
---|---|---|
committer | Koichi Sasada <ko1@atdot.net> | 2020-10-21 07:59:24 +0900 |
commit | 2f50936cb913b7458cbaa03dc4652f1127a7631a (patch) | |
tree | 9840d6ff3141bc0470e65489df640f5ef286329a /ractor.c | |
parent | 587feb0b6e47477ec3b1872de0c951e3d062db98 (diff) | |
download | ruby-2f50936cb913b7458cbaa03dc4652f1127a7631a.tar.gz |
Ractor.make_shareable(obj)
Introduce new method Ractor.make_shareable(obj) which tries to make
obj shareable object. Protocol is here.
(1) If obj is shareable, it is shareable.
(2) If obj is not a shareable object and if obj can be shareable
object if it is frozen, then freeze obj. If obj has reachable
objects (rs), do rs.each{|o| Ractor.make_shareable(o)}
recursively (recursion is not Ruby-level, but C-level).
(3) Otherwise, raise Ractor::Error. Now T_DATA is not a shareable
object even if the object is frozen.
If the method finished without error, given obj is marked as
a sharable object.
To allow makng a shareable frozen T_DATA object, then set
`RUBY_TYPED_FROZEN_SHAREABLE` as type->flags. On default,
this flag is not set. It means user defined T_DATA objects are
not allowed to become shareable objects when it is frozen.
You can make any object shareable by setting FL_SHAREABLE flag,
so if you know that the T_DATA object is shareable (== thread-safe),
set this flag, at creation time for example. `Ractor` object is one
example, which is not a frozen, but a shareable object.
Diffstat (limited to 'ractor.c')
-rw-r--r-- | ractor.c | 413 |
1 files changed, 289 insertions, 124 deletions
@@ -6,8 +6,13 @@ #include "vm_core.h" #include "vm_sync.h" #include "ractor.h" +#include "internal/complex.h" #include "internal/error.h" +#include "internal/hash.h" +#include "internal/rational.h" #include "internal/struct.h" +#include "variable.h" +#include "gc.h" static VALUE rb_cRactor; static VALUE rb_eRactorError; @@ -1743,8 +1748,6 @@ rb_vm_main_ractor_ec(rb_vm_t *vm) return vm->ractor.main_ractor->threads.running_ec; } -#include "ractor.rbinc" - static VALUE ractor_moved_missing(int argc, VALUE *argv, VALUE self) { @@ -1777,128 +1780,6 @@ Init_Ractor(void) rb_obj_freeze(rb_cRactorMovedObject); } -static int -rb_ractor_shareable_p_hash_i(VALUE key, VALUE value, VALUE arg) -{ - // TODO: should we need to avoid recursion to prevent stack overflow? - if (!rb_ractor_shareable_p(key) || !rb_ractor_shareable_p(value)) { - bool *shareable = (bool*)arg; - *shareable = false; - return ST_STOP; - } - return ST_CONTINUE; -} - -static bool -ractor_struct_shareable_members_p(VALUE obj) -{ - VM_ASSERT(RB_TYPE_P(obj, T_STRUCT)); - - long len = RSTRUCT_LEN(obj); - const VALUE *ptr = RSTRUCT_CONST_PTR(obj); - - for (long i=0; i<len; i++) { - if (!rb_ractor_shareable_p(ptr[i])) { - return false; - } - } - return true; -} - -static bool -ractor_obj_ivars_shareable_p(VALUE obj) -{ - uint32_t len = ROBJECT_NUMIV(obj); - VALUE *ptr = ROBJECT_IVPTR(obj); - - for (uint32_t i=0; i<len; i++) { - VALUE val = ptr[i]; - if (val != Qundef && !rb_ractor_shareable_p(ptr[i])) { - return false; - } - } - - return true; -} - -MJIT_FUNC_EXPORTED bool -rb_ractor_shareable_p_continue(VALUE obj) -{ - switch (BUILTIN_TYPE(obj)) { - case T_CLASS: - case T_MODULE: - case T_ICLASS: - goto shareable; - - case T_FLOAT: - case T_COMPLEX: - case T_RATIONAL: - case T_BIGNUM: - case T_SYMBOL: - VM_ASSERT(RB_OBJ_FROZEN_RAW(obj)); - goto shareable; - - case T_STRING: - case T_REGEXP: - if (RB_OBJ_FROZEN_RAW(obj) && - !FL_TEST_RAW(obj, RUBY_FL_EXIVAR)) { - goto shareable; - } - return false; - case T_ARRAY: - if (!RB_OBJ_FROZEN_RAW(obj) || - FL_TEST_RAW(obj, RUBY_FL_EXIVAR)) { - return false; - } - else { - for (int i = 0; i < RARRAY_LEN(obj); i++) { - if (!rb_ractor_shareable_p(rb_ary_entry(obj, i))) return false; - } - goto shareable; - } - case T_HASH: - if (!RB_OBJ_FROZEN_RAW(obj) || - FL_TEST_RAW(obj, RUBY_FL_EXIVAR)) { - return false; - } - else { - bool shareable = true; - rb_hash_foreach(obj, rb_ractor_shareable_p_hash_i, (VALUE)&shareable); - if (shareable) { - goto shareable; - } - else { - return false; - } - } - case T_STRUCT: - if (!RB_OBJ_FROZEN_RAW(obj) || - FL_TEST_RAW(obj, RUBY_FL_EXIVAR)) { - return false; - } - else { - if (ractor_struct_shareable_members_p(obj)) { - goto shareable; - } - else { - return false; - } - } - case T_OBJECT: - if (RB_OBJ_FROZEN_RAW(obj) && ractor_obj_ivars_shareable_p(obj)) { - goto shareable; - } - else { - return false; - } - default: - return false; - } - shareable: - FL_SET_RAW(obj, RUBY_FL_SHAREABLE); - return true; -} - void rb_ractor_dump(void) { @@ -1983,3 +1864,287 @@ rb_ractor_stderr_set(VALUE err) RB_OBJ_WRITE(cr->self, &cr->r_stderr, err); } } + +/// traverse function + +// 2: stop search +// 1: skip child +// 0: continue +typedef int (*rb_obj_traverse_enter_func)(VALUE obj, void *data); +typedef int (*rb_obj_traverse_leave_func)(VALUE obj, void *data); + +struct obj_traverse_data { + rb_obj_traverse_enter_func enter_func; + rb_obj_traverse_leave_func leave_func; + void *data; + + st_table *rec; +}; + + +struct obj_traverse_callback_data { + bool stop; + struct obj_traverse_data *data; +}; + +static int rb_obj_traverse_i(VALUE obj, struct obj_traverse_data *data); + +static int +obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr) +{ + struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr; + + if (rb_obj_traverse_i(key, d->data)) { + d->stop = true; + return ST_STOP; + } + + if (rb_obj_traverse_i(val, d->data)) { + d->stop = true; + return ST_STOP; + } + + return ST_CONTINUE; +} + +static void +obj_tdata_traverse_i(VALUE obj, void *ptr) +{ + struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr; + + if (rb_obj_traverse_i(obj, d->data)) { + d->stop = true; + } +} + +static int +rb_obj_traverse_i(VALUE obj, struct obj_traverse_data *data) +{ + if (RB_SPECIAL_CONST_P(obj)) return 0; + + switch (data->enter_func(obj, data->data)) { + case 0: break; + case 1: return 0; // skip children + case 2: return 1; // stop search + default: rb_bug("rb_obj_traverse_func should return 0 to 2"); + } + + if (st_insert(data->rec, obj, 1)) { + // already traversed + return 0; + } + + if (FL_TEST(obj, FL_EXIVAR)) { + struct gen_ivtbl *ivtbl; + rb_ivar_generic_ivtbl_lookup(obj, &ivtbl); + for (uint32_t i = 0; i < ivtbl->numiv; i++) { + VALUE val = ivtbl->ivptr[i]; + if (val != Qundef && rb_obj_traverse_i(val, data)) return 1; + } + } + + switch (BUILTIN_TYPE(obj)) { + // no child node + case T_STRING: + case T_FLOAT: + case T_BIGNUM: + case T_REGEXP: + case T_FILE: + case T_SYMBOL: + case T_MATCH: + break; + + case T_OBJECT: + { + uint32_t len = ROBJECT_NUMIV(obj); + VALUE *ptr = ROBJECT_IVPTR(obj); + + for (uint32_t i=0; i<len; i++) { + VALUE val = ptr[i]; + if (val != Qundef && rb_obj_traverse_i(val, data)) return 1; + } + } + break; + + case T_ARRAY: + { + for (int i = 0; i < RARRAY_LENINT(obj); i++) { + VALUE e = rb_ary_entry(obj, i); + if (rb_obj_traverse_i(e, data)) return 1; + } + } + break; + + case T_HASH: + { + if (rb_obj_traverse_i(RHASH_IFNONE(obj), data)) return 1; + + struct obj_traverse_callback_data d = { + .stop = false, + .data = data, + }; + rb_hash_foreach(obj, obj_hash_traverse_i, (VALUE)&d); + if (d.stop) return 1; + } + break; + + case T_STRUCT: + { + long len = RSTRUCT_LEN(obj); + const VALUE *ptr = RSTRUCT_CONST_PTR(obj); + + for (long i=0; i<len; i++) { + if (rb_obj_traverse_i(ptr[i], data)) return 1; + } + } + break; + + case T_RATIONAL: + if (rb_obj_traverse_i(RRATIONAL(obj)->num, data)) return 1; + if (rb_obj_traverse_i(RRATIONAL(obj)->den, data)) return 1; + break; + case T_COMPLEX: + if (rb_obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1; + if (rb_obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1; + break; + + case T_DATA: + { + struct obj_traverse_callback_data d = { + .stop = false, + .data = data, + }; + rb_objspace_reachable_objects_from(obj, obj_tdata_traverse_i, &d); + if (d.stop) return 1; + } + break; + + // unreachable + case T_CLASS: + case T_MODULE: + case T_ICLASS: + default: + rp(obj); + rb_bug("unreachable"); + } + + switch (data->leave_func(obj, data->data)) { + case 0: + case 1: return 0; // terminate + case 2: return 1; // stop search + default: rb_bug("rb_obj_traverse_func should return 0 to 2"); + } +} + +// 0: traverse all +// 1: stopped +static int +rb_obj_traverse(VALUE obj, + rb_obj_traverse_enter_func enter_func, + rb_obj_traverse_leave_func leave_func, + void *passed_data) +{ + VALUE h = rb_ident_hash_new(); + + struct obj_traverse_data data = { + .enter_func = enter_func, + .leave_func = leave_func, + .data = passed_data, + .rec = rb_hash_st_table(h), + }; + + int r = rb_obj_traverse_i(obj, &data); + RB_GC_GUARD(h); + return r; +} + +static int +frozen_shareable_p(VALUE obj) +{ + switch (BUILTIN_TYPE(obj)) { + case T_DATA: + if (RTYPEDDATA_P(obj)) { + const rb_data_type_t *type = RTYPEDDATA_TYPE(obj); + if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) { + return true; + } + } + return false; + default: + return true; + } +} + +static int +make_shareable_check_shareable(VALUE obj, void *data) +{ + VM_ASSERT(!SPECIAL_CONST_P(obj)); + + if (RB_OBJ_SHAREABLE_P(obj)) { + return 1; + } + else { + if (!frozen_shareable_p(obj)) { + rb_raise(rb_eRactorError, "can not make shareable object for %"PRIsVALUE, obj); + } + } + + if (!OBJ_FROZEN(obj)) { + rb_funcall(obj, idFreeze, 0); + } + return 0; +} + +static int +mark_shareable(VALUE obj, void *data) +{ + FL_SET_RAW(obj, RUBY_FL_SHAREABLE); + return 0; +} + +VALUE +rb_ractor_make_shareable(VALUE obj) +{ + rb_obj_traverse(obj, + make_shareable_check_shareable, + mark_shareable, + NULL); + return obj; +} + +static int +shareable_p_enter(VALUE obj, void *ptr) +{ + if (RB_OBJ_SHAREABLE_P(obj)) { + return 1; + } + else if (RB_TYPE_P(obj, T_CLASS) || + RB_TYPE_P(obj, T_MODULE) || + RB_TYPE_P(obj, T_ICLASS)) { + // TODO: remove it + mark_shareable(obj, NULL); + return 1; + } + else if (RB_OBJ_FROZEN_RAW(obj) && + frozen_shareable_p(obj)) { + return 0; + } + + return 2; // fail +} + +MJIT_FUNC_EXPORTED bool +rb_ractor_shareable_p_continue(VALUE obj) +{ + if (rb_obj_traverse(obj, + shareable_p_enter, + mark_shareable, + NULL)) { + return false; + } + else { + return true; + } +} + +#include "ractor.rbinc" |