diff options
author | Koichi Sasada <[email protected]> | 2023-03-02 18:27:44 +0900 |
---|---|---|
committer | Koichi Sasada <[email protected]> | 2023-03-03 00:08:02 +0900 |
commit | 5875fce6ce678c444456cc7eed6f28ef000dac24 (patch) | |
tree | d18dce5e9da1c5991b154774a579cd0918a7cef5 | |
parent | 5f3c7ac19674b822c72e09e40cfb58fc71ac501e (diff) |
`Ractor::Selector#empty?`
It returns the waiting set is empty or not.
Also add Ractor::Selector's tests.
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/7417
-rw-r--r-- | bootstraptest/test_ractor.rb | 96 | ||||
-rw-r--r-- | ractor.c | 15 | ||||
-rw-r--r-- | ractor.rb | 12 |
3 files changed, 122 insertions, 1 deletions
diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 3d0fdfa283..5f7cba3947 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -1617,4 +1617,100 @@ assert_match /\Atest_ractor\.rb:1:\s+warning:\s+Ractor is experimental/, %q{ eval("Ractor.new{}.take", nil, "test_ractor.rb", 1) } +## Ractor::Selector + +# Selector#empty? returns true +assert_equal 'true', %q{ + s = Ractor::Selector.new + s.empty? +} + +# Selector#empty? returns false if there is target ractors +assert_equal 'false', %q{ + s = Ractor::Selector.new + s.add Ractor.new{} + s.empty? +} + +# Selector#clear removes all ractors from the waiting list +assert_equal 'true', %q{ + s = Ractor::Selector.new + s.add Ractor.new{10} + s.add Ractor.new{20} + s.clear + s.empty? +} + +# Selector#wait can wait multiple ractors +assert_equal '[10, 20, true]', %q{ + s = Ractor::Selector.new + s.add Ractor.new{10} + s.add Ractor.new{20} + r, v = s.wait + vs = [] + vs << v + r, v = s.wait + vs << v + [*vs.sort, s.empty?] +} + +# Selector#wait can wait multiple ractors with receiving. +assert_equal '30', %q{ + RN = 30 + rs = RN.times.map{ + Ractor.new{ :v } + } + s = Ractor::Selector.new(*rs) + + results = [] + until s.empty? + results << s.wait + + # Note that s.wait can raise an exception because other Ractors/Threads + # can take from the same ractors in the waiting set. + # In this case there is no other takers so `s.wait` doesn't raise an error. + end + + results.size +} + +# Selector#wait can support dynamic addition +assert_equal '600', %q{ + RN = 100 + s = Ractor::Selector.new + rs = RN.times.map{ + Ractor.new{ + Ractor.main << Ractor.new{ Ractor.yield :v3; :v4 } + Ractor.main << Ractor.new{ Ractor.yield :v5; :v6 } + Ractor.yield :v1 + :v2 + } + } + + rs.each{|r| s.add(r)} + h = {v1: 0, v2: 0, v3: 0, v4: 0, v5: 0, v6: 0} + + loop do + case s.wait receive: true + in :receive, r + s.add r + in r, v + h[v] += 1 + break if h.all?{|k, v| v == RN} + end + end + + h.sum{|k, v| v} +} + +# Selector should be GCed (free'ed) withtou trouble +assert_equal 'ok', %q{ + RN = 30 + rs = RN.times.map{ + Ractor.new{ :v } + } + s = Ractor::Selector.new(*rs) + :ok +} + end # if !ENV['GITHUB_WORKFLOW'] @@ -979,7 +979,7 @@ ractor_take_will_lock(rb_ractor_t *r, struct rb_ractor_basket *b) RACTOR_LOCK(r); { - taken =ractor_take_will(r, b); + taken = ractor_take_will(r, b); } RACTOR_UNLOCK(r); @@ -1009,6 +1009,8 @@ ractor_register_take(rb_ractor_t *cr, rb_ractor_t *r, struct rb_ractor_basket *t } else if (!is_take && ractor_take_has_will(r)) { RUBY_DEBUG_LOG("has_will"); + VM_ASSERT(config != NULL); + config->closed = true; } else if (r->sync.outgoing_port_closed) { closed = true; @@ -1515,6 +1517,13 @@ ractor_selector_clear(rb_execution_context_t *ec, VALUE selv) return selv; } +static VALUE +ractor_selector_empty_p(rb_execution_context_t *ec, VALUE selv) +{ + struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv); + return s->take_ractors->num_entries == 0 ? Qtrue : Qfalse; +} + static int ractor_selector_wait_i(st_data_t key, st_data_t val, st_data_t dat) { @@ -1687,6 +1696,10 @@ ractor_selector_wait(rb_execution_context_t *ec, VALUE selv, VALUE do_receivev, } break; } + case basket_type_will: + // no more messages + ractor_selector_remove(ec, selv, taken_basket.sender); + break; default: break; } @@ -429,6 +429,18 @@ class Ractor end # call-seq: + # selector.empty? + # + # Returns true if the number of ractors in the waiting set at the current time is zero. + # + # Note that even if <tt>#empty?</tt> returns false, the subsequent <tt>#wait</tt> + # 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. |