It returns the waiting set is empty or not.

Also add Ractor::Selector's tests.
This commit is contained in:
Koichi Sasada 2023-03-02 18:27:44 +09:00
Родитель 5f3c7ac196
Коммит 5875fce6ce
3 изменённых файлов: 122 добавлений и 1 удалений

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -428,6 +428,18 @@ class Ractor
__builtin_ractor_selector_clear
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]
#