From a2ad0cb7b44a0cb87bd552b312a70bc46016a95f Mon Sep 17 00:00:00 2001 From: Sutou Kouhei Date: Tue, 24 Aug 2021 11:22:00 +0900 Subject: [PATCH] Make Hash#each family usable in Ractor We don't need to increment/decrement iteration level for frozen Hash because frozen Hash can't be modified. We can assume that nobody changes the target Hash while calling #each family. How to reproduce: a = {} 100.times do |i| a[i] = true end Ractor.make_shareable(a) 4.times.collect do Ractor.new(a) do |b| 100.times do b.each_value do end end end end.each(&:take) Example output: internal:ractor>:267: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues. # terminated with exception (report_on_exception is true): # terminated with exception (report_on_exception is true): # terminated with exception (report_on_exception is true): # terminated with exception (report_on_exception is true): /tmp/h.rb:10:in `each_value'/tmp/h.rb:10:in `each_value': : /tmp/h.rb:10:in `each_value'no implicit conversion from nil to integer/tmp/h.rb:10:in `each_value'no implicit conversion from nil to integer (: : (TypeErrorTypeError)no implicit conversion from nil to integer)no implicit conversion from nil to integer ( (TypeErrorTypeError from /tmp/h.rb:10:in `block (3 levels) in
' from /tmp/h.rb:10:in `block (3 levels) in
' )) from /tmp/h.rb:9:in `times' from /tmp/h.rb:9:in `times' from /tmp/h.rb:9:in `block (2 levels) in
' from /tmp/h.rb:10:in `block (3 levels) in
' from /tmp/h.rb:10:in `block (3 levels) in
' from /tmp/h.rb:9:in `block (2 levels) in
' from /tmp/h.rb:9:in `times' from /tmp/h.rb:9:in `times' from /tmp/h.rb:9:in `block (2 levels) in
' from /tmp/h.rb:9:in `block (2 levels) in
' :694:in `take': thrown by remote Ractor. (Ractor::RemoteError) from /tmp/h.rb:14:in `each' from /tmp/h.rb:14:in `
' /tmp/h.rb:10:in `each_value': no implicit conversion from nil to integer (TypeError) from /tmp/h.rb:10:in `block (3 levels) in
' from /tmp/h.rb:9:in `times' from /tmp/h.rb:9:in `block (2 levels) in
' --- hash.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hash.c b/hash.c index 6fa85e81bb..6675b81e65 100644 --- a/hash.c +++ b/hash.c @@ -1503,11 +1503,16 @@ rb_hash_foreach(VALUE hash, rb_foreach_func *func, VALUE farg) if (RHASH_TABLE_EMPTY_P(hash)) return; - hash_iter_lev_inc(hash); arg.hash = hash; arg.func = (rb_foreach_func *)func; arg.arg = farg; - rb_ensure(hash_foreach_call, (VALUE)&arg, hash_foreach_ensure, hash); + if (RB_OBJ_FROZEN(hash)) { + hash_foreach_call((VALUE)&arg); + } + else { + hash_iter_lev_inc(hash); + rb_ensure(hash_foreach_call, (VALUE)&arg, hash_foreach_ensure, hash); + } hash_verify(hash); }