From c9a9b8036c71974ab938a8bb3d6f095d4a318a8e Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 15 Dec 2023 18:25:12 +0900 Subject: [PATCH] remove `Ractor::Selector` from Ruby level `Ractor::Selector` is not approved by Matz so remove it from Ruby-level. The implementation is used by `Ractor.select` so most of implementation was remaind and calling `rb_init_ractor_selector()`, `Ractor::Selector` will be defined. I will provide `ractor-selector` gem to try it. --- bootstraptest/test_ractor.rb | 20 +++++- ractor.c | 113 +++++++++++++++++++++++++------ ractor.rb | 126 ++--------------------------------- 3 files changed, 116 insertions(+), 143 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 7a71f6e071..36ccc005dc 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1401,7 +1401,7 @@ assert_equal '[false, false, true, true]', %q{ } # TracePoint with normal Proc should be Ractor local -assert_equal '[4, 8]', %q{ +assert_equal '[6, 10]', %q{ rs = [] TracePoint.new(:line){|tp| rs << tp.lineno if tp.path == __FILE__}.enable do Ractor.new{ # line 4 @@ -1647,12 +1647,16 @@ assert_match /\Atest_ractor\.rb:1:\s+warning:\s+Ractor is experimental/, %q{ # Selector#empty? returns true assert_equal 'true', %q{ + skip true unless defined? Ractor::Selector + s = Ractor::Selector.new s.empty? } # Selector#empty? returns false if there is target ractors assert_equal 'false', %q{ + skip false unless defined? Ractor::Selector + s = Ractor::Selector.new s.add Ractor.new{} s.empty? @@ -1660,6 +1664,8 @@ assert_equal 'false', %q{ # Selector#clear removes all ractors from the waiting list assert_equal 'true', %q{ + skip true unless defined? Ractor::Selector + s = Ractor::Selector.new s.add Ractor.new{10} s.add Ractor.new{20} @@ -1669,6 +1675,8 @@ assert_equal 'true', %q{ # Selector#wait can wait multiple ractors assert_equal '[10, 20, true]', %q{ + skip [10, 20, true] unless defined? Ractor::Selector + s = Ractor::Selector.new s.add Ractor.new{10} s.add Ractor.new{20} @@ -1678,10 +1686,12 @@ assert_equal '[10, 20, true]', %q{ r, v = s.wait vs << v [*vs.sort, s.empty?] -} +} if defined? Ractor::Selector # Selector#wait can wait multiple ractors with receiving. assert_equal '30', %q{ + skip 30 unless defined? Ractor::Selector + RN = 30 rs = RN.times.map{ Ractor.new{ :v } @@ -1698,11 +1708,13 @@ assert_equal '30', %q{ end results.size -} +} if defined? Ractor::Selector # Selector#wait can support dynamic addition yjit_enabled = ENV.key?('RUBY_YJIT_ENABLE') || ENV.fetch('RUN_OPTS', '').include?('yjit') || BT.ruby.include?('yjit') assert_equal '600', %q{ + skip 600 unless defined? Ractor::Selector + RN = 100 s = Ractor::Selector.new rs = RN.times.map{ @@ -1732,6 +1744,8 @@ assert_equal '600', %q{ # Selector should be GCed (free'ed) without trouble assert_equal 'ok', %q{ + skip :ok unless defined? Ractor::Selector + RN = 30 rs = RN.times.map{ Ractor.new{ :v } diff --git a/ractor.c b/ractor.c index 34fb7d5e07..8a7154ac27 100644 --- a/ractor.c +++ b/ractor.c @@ -1476,10 +1476,10 @@ RACTOR_SELECTOR_PTR(VALUE selv) // Ractor::Selector.new static VALUE -ractor_selector_create(VALUE crv) +ractor_selector_create(VALUE klass) { struct rb_ractor_selector *s; - VALUE selv = TypedData_Make_Struct(rb_cRactorSelector, struct rb_ractor_selector, &ractor_selector_data_type, s); + VALUE selv = TypedData_Make_Struct(klass, struct rb_ractor_selector, &ractor_selector_data_type, s); s->take_basket.type.e = basket_type_reserved; s->take_ractors = st_init_numtable(); // ractor (ptr) -> take_config return selv; @@ -1488,7 +1488,7 @@ ractor_selector_create(VALUE crv) // Ractor::Selector#add(r) static VALUE -ractor_selector_add(rb_execution_context_t *ec, VALUE selv, VALUE rv) +ractor_selector_add(VALUE selv, VALUE rv) { if (!rb_ractor_p(rv)) { rb_raise(rb_eArgError, "Not a ractor object"); @@ -1506,7 +1506,7 @@ ractor_selector_add(rb_execution_context_t *ec, VALUE selv, VALUE rv) config->closed = false; config->oneshot = false; - if (ractor_register_take(rb_ec_ractor_ptr(ec), r, &s->take_basket, false, config, true)) { + if (ractor_register_take(GET_RACTOR(), r, &s->take_basket, false, config, true)) { st_insert(s->take_ractors, (st_data_t)r, (st_data_t)config); } @@ -1516,7 +1516,7 @@ ractor_selector_add(rb_execution_context_t *ec, VALUE selv, VALUE rv) // Ractor::Selector#remove(r) static VALUE -ractor_selector_remove(rb_execution_context_t *ec, VALUE selv, VALUE rv) +ractor_selector_remove(VALUE selv, VALUE rv) { if (!rb_ractor_p(rv)) { rb_raise(rb_eArgError, "Not a ractor object"); @@ -1549,28 +1549,24 @@ struct ractor_selector_clear_data { static int ractor_selector_clear_i(st_data_t key, st_data_t val, st_data_t data) { - struct ractor_selector_clear_data *ptr = (struct ractor_selector_clear_data *)data; + VALUE selv = (VALUE)data; rb_ractor_t *r = (rb_ractor_t *)key; - ractor_selector_remove(ptr->ec, ptr->selv, r->pub.self); + ractor_selector_remove(selv, r->pub.self); return ST_CONTINUE; } static VALUE -ractor_selector_clear(rb_execution_context_t *ec, VALUE selv) +ractor_selector_clear(VALUE selv) { - struct ractor_selector_clear_data data = { - .selv = selv, - .ec = ec, - }; struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv); - st_foreach(s->take_ractors, ractor_selector_clear_i, (st_data_t)&data); + st_foreach(s->take_ractors, ractor_selector_clear_i, (st_data_t)selv); st_clear(s->take_ractors); return selv; } static VALUE -ractor_selector_empty_p(rb_execution_context_t *ec, VALUE selv) +ractor_selector_empty_p(VALUE selv) { struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv); return s->take_ractors->num_entries == 0 ? Qtrue : Qfalse; @@ -1642,8 +1638,9 @@ ractor_selector_wait_cleaup(rb_ractor_t *cr, void *ptr) } static VALUE -ractor_selector_wait(rb_execution_context_t *ec, VALUE selv, VALUE do_receivev, VALUE do_yieldv, VALUE yield_value, VALUE move) +ractor_selector__wait(VALUE selv, VALUE do_receivev, VALUE do_yieldv, VALUE yield_value, VALUE move) { + rb_execution_context_t *ec = GET_EC(); struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv); struct rb_ractor_basket *tb = &s->take_basket; struct rb_ractor_basket taken_basket; @@ -1739,7 +1736,7 @@ ractor_selector_wait(rb_execution_context_t *ec, VALUE selv, VALUE do_receivev, case basket_type_yielding: rb_bug("unreachable"); case basket_type_deleted: { - ractor_selector_remove(ec, selv, taken_basket.sender); + ractor_selector_remove(selv, taken_basket.sender); rb_ractor_t *r = RACTOR_PTR(taken_basket.sender); if (ractor_take_will_lock(r, &taken_basket)) { @@ -1755,7 +1752,7 @@ ractor_selector_wait(rb_execution_context_t *ec, VALUE selv, VALUE do_receivev, } case basket_type_will: // no more messages - ractor_selector_remove(ec, selv, taken_basket.sender); + ractor_selector_remove(selv, taken_basket.sender); break; default: break; @@ -1769,6 +1766,60 @@ ractor_selector_wait(rb_execution_context_t *ec, VALUE selv, VALUE do_receivev, return rb_ary_new_from_args(2, ret_r, ret_v); } +static VALUE +ractor_selector_wait(int argc, VALUE *argv, VALUE selector) +{ + VALUE options; + ID keywords[3]; + VALUE values[3]; + + keywords[0] = rb_intern("receive"); + keywords[1] = rb_intern("yield_value"); + keywords[2] = rb_intern("move"); + + rb_scan_args(argc, argv, "0:", &options); + rb_get_kwargs(options, keywords, 0, numberof(values), values); + return ractor_selector__wait(selector, + values[0] == Qundef ? Qfalse : RTEST(values[0]), + values[1] != Qundef, values[1], values[2]); +} + +static VALUE +ractor_selector_new(int argc, VALUE *ractors, VALUE klass) +{ + VALUE selector = ractor_selector_create(klass); + + for (int i=0; i#empty? returns false, the subsequent #wait - # may raise an exception because other ractors may close the target ractors. - # - def empty? - __builtin_ractor_selector_empty_p - end - - # call-seq: - # selector.wait(receive: false, yield_value: yield_value, move: false) -> [ractor or symbol, value] - # - # Waits Ractor events. It is lighter than Ractor.select() for many ractors. - # - # The simplest form is waiting for taking a value from one of - # registerred ractors like that. - # - # selector = Ractor::Selector.new(r1, r2, r3) - # r, v = selector.wait - # - # On this case, when r1, r2 or r3 is ready to take (yielding a value), - # this method takes the value from the ready (yielded) ractor - # and returns [the yielded ractor, the taking value]. - # - # Note that if a take target ractor is closed, the ractor will be removed - # automatically. - # - # If you also want to wait with receiving an object from other ractors, - # you can specify receive: true keyword like: - # - # r, v = selector.wait receive: true - # - # On this case, wait for taking from r1, r2 or r3 and waiting for receving - # a value from other ractors. - # If it successes the receiving, it returns an array object [:receive, the received value]. - # - # If you also want to wait with yielding a value, you can specify - # :yield_value like: - # - # r, v = selector.wait yield_value: obj - # - # On this case wait for taking from r1, r2, or r3 and waiting for taking - # yielding value (obj) by another ractor. - # If antoher ractor takes the value (obj), it returns an array object [:yield, nil]. - # - # You can specify a keyword parameter move: true like Ractor.yield(obj, move: true) - # - def wait receive: false, yield_value: yield_unspecified = true, move: false - __builtin_ractor_selector_wait receive, !yield_unspecified, yield_value, move - end + __builtin_ractor_select_internal ractors, do_receive, !yield_unspecified, yield_value, move end #