Project

General

Profile

Actions

Bug #19369

open

Small corner-case issue that breaks Ractor isolation: change cloned object from another thread

Bug #19369: Small corner-case issue that breaks Ractor isolation: change cloned object from another thread

Added by luke-gru (Luke Gruber) almost 3 years ago. Updated 6 months ago.

Status:
Assigned
Assignee:
Target version:
-
[ruby-core:111979]

Description

I was looking into how objects are traversed for deep cloning and I came up with a way to break it. I don't think it'll ever happen in real life so it's not really an issue, just
an interesting case. Run with warnings disabled.

obj = Object.new p "unshareable obj:", obj UNSHAREABLE = obj GO = false SET = false class Object attr_accessor :unshareable def initialize_clone(orig) puts "Clone called for #{orig.inspect}, self = #{self.inspect}" _self = self if orig == UNSHAREABLE t = Thread.new do puts "In thread" Thread.pass until GO puts "Setting unshareable!" # this must be done in separate thread to bypass object traversal deep-cloning _self.unshareable = UNSHAREABLE Object.const_set(:SET, true) end end super(orig) end end r = Ractor.new(obj) do |o| puts "from r#{Ractor.current.object_id} obj #{o.inspect}" GO = true loop until SET p "from ractor, got unshareable:", o.unshareable end r.take 

Updated by luke-gru (Luke Gruber) almost 3 years ago Actions #1 [ruby-core:111980]

If you wanted to fix this one way would be to disable thread creation in the current ractor (main in this case) while deep-cloning in Ractor.new or Ractor#send. Another idea would be to not call initialize_clone during deep-cloning for Ractor.new or Ractor#send, but that would be a negative change IMO.

BTW, forgot to mention to run the above ruby code with warnings disabled.

Updated by luke-gru (Luke Gruber) almost 3 years ago Actions #2 [ruby-core:112016]

I made a patch for this here: https://github.com/luke-gru/ruby/commit/ad760be949c2a35aac71ac0ff8dea4ec3169f589. I'll send PR if it's an acceptable solution.

Edit: My commit doesn't actually fix this in the proper way, because you could do:

def initialize_clone(orig) if orig == UNSHAREABLE begin Thread.new { } rescue ThreadError t = Thread.new do "..." end end end end A proper is a bit tricky. 

Updated by hsbt (Hiroshi SHIBATA) almost 3 years ago Actions #3 [ruby-core:112017]

  • Status changed from Open to Assigned
  • Assignee set to ko1 (Koichi Sasada)

Updated by jhawthorn (John Hawthorn) 6 months ago Actions #4

  • Assignee changed from ko1 (Koichi Sasada) to ractor
Actions

Also available in: PDF Atom