
Issue #21309 has been updated by Eregon (Benoit Daloze). At least for Queue it's not that simple, because it contains objects, and the invariants of Ractor (without which it would just segfault) are: 1. no object can be accessed by multiple Ractors, unless it is a shareable object. 2. Shareable objects can only refer to other shareable objects (otherwise it trivially breaks 1.). Shareable objects are also typically immutable, or avoid exposing mutability to other Ractors, otherwise it introduces race conditions again and loses benefits of the actor model. So you could have a Queue which only accepts shareable objects, but that wouldn't work for Timeout because the Request objects are mutable. Or you could have a Queue which Ractor-moves objects, but that doesn't work for Timeout either because it [keeps a reference to the enqueued object and uses it](https://github.com/ruby/timeout/blob/607d8c6fbe4d86db1cf22846e89198b47cec716...). Also both of these alternatives are incompatible for non-Ractor semantics if applied to the core ::Queue itself. I think @ko1 has a plan for Timeout specifically using Ractor-local storage, I'm waiting for his PR, I hope it won't make the code too complicated/messy. My impression is yes it's (sometimes very) hard to make existing Ruby code Ractor-compatible, and it's due to the Ractor/actor programming model. IMO Rubyists should just use threads, and if they want them to run in parallel use TruffleRuby or JRuby or request harder for CRuby to remove the GVL (CPython has done it, so it is feasible). ---------------------------------------- Feature #21309: Can Thread::Mutex be Ractor shareable? https://bugs.ruby-lang.org/issues/21309#change-112894 * Author: osyoyu (Daisuke Aritomo) * Status: Open ---------------------------------------- ## Background Keeping a `Mutex` object in a constant or a class instance variable is a common pattern seen in code with thread safety in mind. However, this kind of code does not play well with Ractors: ```ruby require 'thread' class C MUTEX = Mutex.new def self.foo MUTEX.synchronize { p 1 } end end Ractor.new { C.foo }.take ``` ``` t.rb:11: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues. #<Thread:0x000000011d80f368 run> terminated with exception (report_on_exception is true): t.rb:7:in 'C.foo': can not access non-shareable objects in constant C::MUTEX by non-main ractor. (Ractor::IsolationError) from t.rb:12:in 'block in <main>' <internal:ractor>:711:in 'Ractor#take': thrown by remote Ractor. (Ractor::RemoteError) from t.rb:13:in '<main>' t.rb:7:in 'C.foo': can not access non-shareable objects in constant C::MUTEX by non-main ractor. (Ractor::IsolationError) from t.rb:12:in 'block in <main>' ``` Many libraries follow this pattern. `Mutex` not being Ractor shareable is blocking these libraries from being used from inside Ractors. `Timeout` in stdlib in particular has large impact since it is required from many other gems by default, including `net/http`. https://github.com/ruby/timeout/blob/v0.4.3/lib/timeout.rb#L49-L50 https://github.com/lostisland/faraday/blob/v2.13.1/lib/faraday/middleware.rb... ## Proposal Make built-in concurrency primitives (Thread::Mutex, Thread::ConditionVariable and Thread::Queue) Ractor shareable. While this idea may not be strictly aligned with idea of the Ractor world (exchanging messages for controlling concurrency?), I have the feeling that too many code is blocked from running in Ractors because `Mutex` is not Ractor shareable. Allowing `Mutex`es to be shared would make a large portion of existing Ruby code Ractor-compatible, or at least make migration much easier. I believe that it won't be semantically incorrect, since they are concurrency primitives after all. One thing to consider that the current `Mutex` implementation is based on the GVL (I believe so). Migration to some other implementation e.g. pthread_mutex or CRITICAL_SECTION may be needed to make Mutex work well on Ractors. -- https://bugs.ruby-lang.org/