Project

General

Profile

Actions

Feature #20627

closed

`require` on Ractor should run on the main Ractor

Added by ko1 (Koichi Sasada) 5 months ago. Updated 18 days ago.

Status:
Closed
Target version:
-
[ruby-core:118542]

Description

Now require on main Ractor is not allowed (raising error) but it is hard, especially for autoload.
So let's allow require by running it on the main Ractor.

Background

On many libraries it is needed to run loading on the main Ractors because:

  1. Setup constants with unshareable objects (such as C = []) are not allowed on non main Ractors.
  2. Setup global variables and class variables are not allowed. $LOADED_FEATURES is also untouchable.
  3. (maybe more reasons)

So the require on non main Ractors is not allowed.

However it is hard to program especially on autoload.
Also dynamic require (require in methods) are not allowed too (pp method, for example).

Proposal

Allow require on non main Ractors by running require process on the main Ractor.

(quoted on my talk at RubyKaigi 2024)

rb_ractor_interrupt_exec(target_ractor, func) C-API

Make a thread on target_ractor and run func (C function) on it.

I think it is safe to expose on Ruby API because running func on a newly created thread (do not disturb running target threads). But now it is proposed as only (hidden) C-API.

New Ractor methods

  • Ractor.main? returns Ractors
  • Ractor.require(feature) do require on the main Ractor

These new methods are useful for users who override require method like RubyGems.

alias orig_require require

def require feature
  return Rator.require(feature) if defined?(Ractor.main?) && !Ractor.main?

  # overriding require code
end

Or we can prepend a module like:

Module.new do
  def require(feature)
    return Rator.require(feature) if defined?(Ractor.main?) && !Ractor.main?
    super(feature)
  end
  Kernel.prepend self
end  

will support ractors for all overgrinding methods. But not sure it is acceptable to add additional one modules in ancestors by prepend.

Also this technique doesn't support require overriding by prepending.

Implementation

https://github.com/ruby/ruby/pull/11142 (not matured yet)


Files

clipboard-202407111201-kaiem.png (70.8 KB) clipboard-202407111201-kaiem.png ko1 (Koichi Sasada), 07/11/2024 03:01 AM
Actions #1

Updated by ko1 (Koichi Sasada) 5 months ago

  • Description updated (diff)
Actions #2

Updated by ko1 (Koichi Sasada) 5 months ago

  • Description updated (diff)
Actions #3

Updated by ko1 (Koichi Sasada) 5 months ago

  • Description updated (diff)

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

Now require on main Ractor is not allowed (raising error)

I think you mean non-main Ractor. I like this change, I think it's necessary to get wider adoption for Ractors due to the autoload issue you mentioned. The change should be documented well in the require docs and the Ractor docs once the implementation is mature since it's a fairly major change imo.

Actions #5

Updated by jeremyevans0 (Jeremy Evans) 4 months ago

  • Tracker changed from Bug to Feature
  • Backport deleted (3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN)

Updated by ko1 (Koichi Sasada) 3 months ago

I want to add new features:

  • Ractor._activated which is called when the first Ractor.new is called
  • Ractor._require(feature) described in this issue
  • _activated method uses prepend with new anonymous module to call Ractor._require when the require is called on non-main Ractors.
class Ractor
  class << self
    private

    # internal method
    def _require feature
      if main?
        super feature
      else
        Primitive.ractor_require feature
      end
    end

    # internal method that is called when the first "Ractor.new" is called
    def _activated
      Kernel.prepend Module.new{|m|
        m.set_temporary_name '<RactorRequire>'

        def require feature
          if Ractor.main?
            super
          else
            Ractor.__send__ :_require, feature
          end
        end
      }
    end
  end

I think most of require can be ractor supported.
If a library uses same technique (prepend to override require), the library should call Ractor._require by itself.

Actions #7

Updated by hsbt (Hiroshi SHIBATA) 2 months ago

  • Status changed from Open to Assigned
Actions #8

Updated by ko1 (Koichi Sasada) 18 days ago

  • Status changed from Assigned to Closed

Applied in changeset git|aa63699d10e489bc6d9c13406fc47f581001568b.


support require in non-main Ractors

Many libraries should be loaded on the main ractor because of
setting constants with unshareable objects and so on.

This patch allows to call requore on non-main Ractors by
asking the main ractor to call require on it. The calling ractor
waits for the result of require from the main ractor.

If the require call failed with some reasons, an exception
objects will be deliverred from the main ractor to the calling ractor
if it is copy-able.

Same on require_relative and require by autoload.

Now Ractor.new{pp obj} works well (the first call of pp requires
pp library implicitly).

[Feature #20627]

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0