Project

General

Profile

Feature #17414

Ractor should allow access to shareable attributes for Modules/Classes

Added by marcandre (Marc-Andre Lafortune) 3 months ago. Updated 3 months ago.

Status:
Open
Priority:
Normal
Target version:
-
[ruby-core:101566]

Description

Current situation is very limiting.

Use-case: global config.

Example: yaml has a global config and it's not clear to me how to make that Ractor-aware (nicely).

It is possible to have the same effect but in ugly ways:

# Using instance variables of Module not allowed:

module Config
  class << self
    attr_accessor :conf
  end
  self.conf = 42
end

Ractor.new { Config.conf = 66 }.take # => can not access instance variables from non-main Ractors
Ractor.new { puts Config.conf }.take # => can not access instance variables from non-main Ractors

# Same functionality using constants allowed:
module Config
  class << self
    def conf
      CONF
    end

    def conf=(new_conf)
      remove_const(:CONF)
      const_set(:CONF, new_conf)
    end
  end
  CONF = 42
end

Ractor.new { Config.conf = 66 }.take # => ok
Ractor.new { puts Config.conf }.take # => 66

# Same functionality using methods allowed:
module Config
  class << self
    def conf
      42
    end

    def conf=(new_conf)
      singleton_class.undef_method(:conf)
      define_singleton_method(:conf, &Ractor.make_shareable(Proc.new { new_conf }))
    end
  end
end

Ractor.new { Config.conf = 66 }.take # => ok
Ractor.new { puts Config.conf }.take # => 66

The priority would be to allow reading these instance variables if they are shareable. Ideally writing would also be allowed, but limiting that to main ractor is less probablematic than with reading.

Updated by Eregon (Benoit Daloze) 3 months ago

Sounds a bit to me like a case where Thread makes more sense than Ractor (and they are far more compatible with existing code).

Wanting to mutate global state is explicitly against the principle of (recent) actor models, isn't it?
(even if only the main Ractor can write, it's global state).

Unfortunately, an approach like e.g. in Erlang to have extra actors for representing global state doesn't work well, since each Ractor needs its own native thread.

Updated by marcandre (Marc-Andre Lafortune) 3 months ago

Eregon (Benoit Daloze) wrote in #note-1:

Sounds a bit to me like a case where Thread makes more sense than Ractor

I am not sure I understand your point of view.

Using yaml/psych should be doable easily in multiple Ractors, right?

Here, the config is used to register domain specific handlers.

Having the global config be per-Ractor would complicate the design for no gain. I think allowing the main Ractor to change the global config, and all Ractors to read the global config seems the like the way to go.

At any point in time, the global config will be an immutable data structure. After the config has changed, a different immutable data structure will be returned.

The most natural way to implement is using singleton class attributes.

Updated by Eregon (Benoit Daloze) 3 months ago

marcandre (Marc-Andre Lafortune) wrote in #note-2:

Using yaml/psych should be doable easily in multiple Ractors, right?

Ideally, but I guess there might be many issues with existing gems and the restrictions of Ractor.

Having the global config be per-Ractor would complicate the design for no gain.

It depends what you do with Ractor. If it was hosting multiple apps in the same process, it might make a lot of sense.

At any point in time, the global config [...]

I think Ractor compatibility is a good opportunity to actually revisit whether things need to be global.

In many (all?) cases, it does not need to be global.
For instance, such domain specific handlers could be passed explicitly to YAML.load/dump, which would be much clearer and cleaner.

Also available in: Atom PDF