Project

General

Profile

Actions

Bug #20078

closed

StringIO cannot be moved between Ractors

Added by forthoney (Seong-Heon Jung) 11 months ago. Updated 11 months ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:115845]

Description

The following code will raise an unusual Ractor error.

require 'stringio'
r = Ractor.new { loop { Ractor.receive } }
Ractor.shareable?(StringIO.new) #=> false
r.send(StringIO.new) # passing it via copy works
r.send(StringIO.new, move: true) # <internal:ractor>:587:in `send': can not move StringIO object. (Ractor::Error)

I'm not 100% sure but I believe that this probably not the intended behavior considering

  • The error raised is a generic Ractor::Error rather than something specific like Ractor::MovedError or Ractor::IsolationError
  • It can be copied
  • No documentation exists for this error
  • I have yet to seen this happen on any other class instance
  • Typo (can not -> cannot)

Updated by matz (Yukihiro Matsumoto) 11 months ago

StringIO is a mutable object, so it is fundamentally not movable. I agree that some cosmetic errors you have pointed should be fixed.

Matz.

Updated by forthoney (Seong-Heon Jung) 11 months ago

Thank you for the quick response!
If my understanding is correct, then there are 4 categories of Ruby objects:

  1. Shareable objects (Ractors, immutable objects, etc)
  2. Unshareable but sendable with copy and move
  3. Unshareable but sendable with only copy (StringIO)
  4. Unshareable and unsendable objects (Thread)

The documentation makes a fairly clear distinction between shareable and unshareable, but the line between the three different types of unshareable objects is a bit unclear.
What are the criteria for the different degrees of "sendability"?

Updated by luke-gru (Luke Gruber) 11 months ago

I think this is due to not currently being able to move any T_DATA objects between ractors. StringIO is defined in a C extension and is a T_DATA object internally. See https://github.com/ruby/ruby/blob/master/ractor.c#L3417C1-L3417C13. This isn't a fundamental limitation (I don't think?), it's just it hasn't been implemented yet. But certain errors could happen, for example if the C extension allows you to move an object to another Ractor, but then uses this same object internally in the original Ractor, maybe it could cause errors.

The documentation should mention this, without going into too much detail. Something like:
Currently, most types that are implemented in C extensions cannot be moved.

Updated by forthoney (Seong-Heon Jung) 11 months ago

luke-gru (Luke Gruber) wrote in #note-3:

I think this is due to not currently being able to move any T_DATA objects between ractors. StringIO is defined in a C extension and is a T_DATA object internally. See https://github.com/ruby/ruby/blob/master/ractor.c#L3417C1-L3417C13. This isn't a fundamental limitation (I don't think?), it's just it hasn't been implemented yet. But certain errors could happen, for example if the C extension allows you to move an object to another Ractor, but then uses this same object internally in the original Ractor, maybe it could cause errors.

The documentation should mention this, without going into too much detail. Something like:
Currently, most types that are implemented in C extensions cannot be moved.

I agree. The documentation does mention T_DATA, but does not really mention the implications of being unable to move T_DATA, which is that "most types that are implemented in C extensions cannot be moved".

Additionally, I am wondering if it makes sense to make a Ractor.movable? module function similar to Ractor.shareable? This would greatly save developer time since they can check if a object is movable in IRB rather than having to dig through the source code.

Updated by luke-gru (Luke Gruber) 11 months ago

I created a PR that updates the Ractor docs: https://github.com/ruby/ruby/pull/9395. It doesn't go into much detail about the situation but clarifies it a bit.

Adding a movable? method would have have to be a separate ticket @forthoney (Seong-Heon Jung). I think it's an okay idea, but I'm a bit concerned because then should we add a copyable? method too? We could just have a sendable? method that takes the move keyword, which could deal with both copying and moving cases. I think unit or integration tests would catch most cases of sending the wrong objects across Ractors, but I see a use for it.

Updated by luke-gru (Luke Gruber) 11 months ago

I think this ticket can be closed as I've updated the docs to reflect the current situation and it's been merged.

Actions #7

Updated by jeremyevans0 (Jeremy Evans) 11 months ago

  • Status changed from Open to Closed
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like1Like1Like0Like0