Class: Farce::ModeManager
- Inherits:
-
Object
- Object
- Farce::ModeManager
- Includes:
- Internal::Copyable, Shareable::Immutable
- Defined in:
- lib/farce/mode_manager.rb,
lib/farce/mode_manager/smart_rule.rb
Overview
A mode manager encapsulates logic for dealing with Ruby objects that aren't Ractor-shareable. It is a perfect companion piece for APIs that require Ractor-shareable objects.
For instance, Ratomic::Queue expects Ractor-shareable objects without checking for them. Creating a thin wrapper with a mode manager allows you to use it safely and with non-shareable objects.
require "ratomic"
require "farce"
class MyQueue
include Farce::Shareable
def initialize(capacity: 1024, mode: :smart)
@queue = Ratomic::Queue.new(capacity)
@manager = Farce::ModeManager.new(mode:)
end
def pop = @manager.unwrap(@queue.pop)
def push(value, mode: nil)
@queue.push(@manager.wrap(value, mode:))
self
end
end
queue = MyQueue.new(capacity: 100, mode: :move)
# consume the queue on a different Ractor
Ractor.new(queue) { |q| p Ractor.shareable?(q.pop) }
object = Object.new
q.push(object)
# push moved the object into the queue, so it is no longer accessible on the current Ractor
Ractor::MovedObject === object # => true
It is recommended to use a different mode manager for each instance of a queue, port, or whatever else you're wrapping, as #wrap may create an Envelope and #unwrap will only unwrap envelopes created by the same mode manager. This way users can still safely send envelopes they created between Ractors without them getting unexpectedly claimed.
Valid modes are:
:smart- Applies smart rules or a dedicated proxy. Raises for unsupported values. This is the default for most mode-backed classes.:smart_raise- Applies smart rules or a dedicated proxy, with a raising fallback.:smart_copy- Applies smart rules or a dedicated proxy, then falls back to copying or Marshal. This is the default for Port.:smart_move- Applies smart rules or a dedicated proxy, then falls back to moving.:copy- The value will be copied between Ractors.:marshal- The value will be serialized immediately and reconstructed once per Ractor with Ruby's Marshal protocol. Contents must support Marshal. Nested shareable values may be reconstructed too.:make_shareable- The value will be made Ractor-shareable using Ractor.make_shareable.:move- The value will be moved between Ractors. This saves memory compared to copying, and supports values that can't be copied but moved (like IO objects). However, the value will no longer be accessible on the Ractor that pushed it.:mutable- A Mutable instance will be created for the value. This isn't done recursively and thus will fail for nested unshareable values.:local- The value will be kept local to the Ractor that pushed it. Another ractor trying to receive it will get an error. Useful for usage contained within a single Ractor.:proxy- The value will be wrapped in aFarce::Proxythat executes calls in the original Ractor.:raise- An error will be raised if the value is not Ractor-shareable. Useful for enforcing shareability.:dedup- The value will be deduplicated using dedup, then made Ractor-shareable. This may update and freeze the original. Already-shareable values pass through unchanged.:shareable_copy- The value will be copied and the copy will be made Ractor-shareable.
Defined Under Namespace
Classes: SmartRule
Instance Attribute Summary collapse
-
#mode ⇒ Symbol
readonly
The default mode to use when wrapping values.
-
#register ⇒ Farce::Proxy::Register?
readonly
The register used for generating Farce::Proxy instances.
Instance Method Summary collapse
-
#initialize(mode: :smart, register: nil) ⇒ ModeManager
constructor
Valid modes are: *
:smart- Applies smart rules or a dedicated proxy. -
#managed_envelope?(value) ⇒ Boolean
Whether the value is an envelope managed by this mode manager.
-
#same_value?(left, right, identity: false) ⇒ Boolean
Compares two values managed by this instance.
-
#unwrap(value) ⇒ BasicObject
Unwraps any envelope created by this mode manager, returning the value inside.
-
#wrap(value, mode: nil) ⇒ BasicObject, Envelope
Wraps a value in an envelope if it is not Ractor-shareable.
Methods included from Shareable
Methods included from Internal::Copyable
Constructor Details
#initialize(mode: :smart, register: nil) ⇒ ModeManager
Valid modes are:
:smart- Applies smart rules or a dedicated proxy. Raises for unsupported values. This is the default for most mode-backed classes.:smart_raise- Applies smart rules or a dedicated proxy, with a raising fallback.:smart_copy- Applies smart rules or a dedicated proxy, then falls back to copying or Marshal. This is the default for Port.:smart_move- Applies smart rules or a dedicated proxy, then falls back to moving.:copy- The value will be copied between Ractors.:marshal- The value will be serialized immediately and reconstructed once per Ractor with Ruby's Marshal protocol. Contents must support Marshal. Nested shareable values may be reconstructed too.:make_shareable- The value will be made Ractor-shareable using Ractor.make_shareable.:move- The value will be moved between Ractors. This saves memory compared to copying, and supports values that can't be copied but moved (like IO objects). However, the value will no longer be accessible on the Ractor that pushed it.:mutable- A Farce::Mutable instance will be created for the value. This isn't done recursively and thus will fail for nested unshareable values.:local- The value will be kept local to the Ractor that pushed it. Another ractor trying to receive it will get an error. Useful for usage contained within a single Ractor.:proxy- The value will be wrapped in aFarce::Proxythat executes calls in the original Ractor.:raise- An error will be raised if the value is not Ractor-shareable. Useful for enforcing shareability.:dedup- The value will be deduplicated using Farce.dedup, then made Ractor-shareable. This may update and freeze the original. Already-shareable values pass through unchanged.:shareable_copy- The value will be copied and the copy will be made Ractor-shareable.
66 67 68 69 70 71 |
# File 'lib/farce/mode_manager.rb', line 66 def initialize(mode: :smart, register: nil) raise ArgumentError, "invalid mode: #{mode.inspect}" unless Farce::MODES.include?(mode) @mode = mode @register = register super() end |
Instance Attribute Details
#mode ⇒ Symbol (readonly)
Returns The default mode to use when wrapping values.
58 59 60 |
# File 'lib/farce/mode_manager.rb', line 58 def mode @mode end |
#register ⇒ Farce::Proxy::Register? (readonly)
Returns The register used for generating Farce::Proxy instances.
61 62 63 |
# File 'lib/farce/mode_manager.rb', line 61 def register @register end |
Instance Method Details
#managed_envelope?(value) ⇒ Boolean
Returns Whether the value is an envelope managed by this mode manager.
149 |
# File 'lib/farce/mode_manager.rb', line 149 def managed_envelope?(value) = Envelope === value && value.auto_unwrap.equal?(self) |
#same_value?(left, right, identity: false) ⇒ Boolean
Compares two values managed by this instance. Compatible envelopes can be compared without claiming or opening them.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/farce/mode_manager.rb', line 129 def same_value?(left, right, identity: false) left_managed = managed_envelope?(left) right_managed = managed_envelope?(right) if left_managed && right_managed return false if identity && Envelope::Move === left && Envelope::Local === right && !left.owned? return left.same_value?(right, identity:) end return false if identity && left_managed return left.same_value?(right) if left_managed left = unwrap(left) right = unwrap(right) return BasicObject.instance_method(:equal?).bind_call(left, right) if identity left == right end |
#unwrap(value) ⇒ BasicObject
Unwraps any envelope created by this mode manager, returning the value inside. Other options, including other envelopes, will be returned as-is.
118 119 120 121 |
# File 'lib/farce/mode_manager.rb', line 118 def unwrap(value) return value unless managed_envelope?(value) value.value end |
#wrap(value, mode: nil) ⇒ BasicObject, Envelope
Wraps a value in an envelope if it is not Ractor-shareable.
Valid modes are:
:smart- Applies smart rules or a dedicated proxy. Raises for unsupported values. This is the default for most mode-backed classes.:smart_raise- Applies smart rules or a dedicated proxy, with a raising fallback.:smart_copy- Applies smart rules or a dedicated proxy, then falls back to copying or Marshal. This is the default for Port.:smart_move- Applies smart rules or a dedicated proxy, then falls back to moving.:copy- The value will be copied between Ractors.:marshal- The value will be serialized immediately and reconstructed once per Ractor with Ruby's Marshal protocol. Contents must support Marshal. Nested shareable values may be reconstructed too.:make_shareable- The value will be made Ractor-shareable using Ractor.make_shareable.:move- The value will be moved between Ractors. This saves memory compared to copying, and supports values that can't be copied but moved (like IO objects). However, the value will no longer be accessible on the Ractor that pushed it.:mutable- A Farce::Mutable instance will be created for the value. This isn't done recursively and thus will fail for nested unshareable values.:local- The value will be kept local to the Ractor that pushed it. Another ractor trying to receive it will get an error. Useful for usage contained within a single Ractor.:proxy- The value will be wrapped in aFarce::Proxythat executes calls in the original Ractor.:raise- An error will be raised if the value is not Ractor-shareable. Useful for enforcing shareability.:dedup- The value will be deduplicated using Farce.dedup, then made Ractor-shareable. This may update and freeze the original. Already-shareable values pass through unchanged.:shareable_copy- The value will be copied and the copy will be made Ractor-shareable.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/farce/mode_manager.rb', line 92 def wrap(value, mode: nil) return value if Ractor.shareable?(value) case mode ||= self.mode when :smart, :smart_copy, :smart_move, :smart_raise return Ractor.make_shareable(-value) if value.is_a?(String) SMART_RULES[value.class].call(value, self, mode) when :copy then Envelope::Copy.new(value, self) when :dedup then Ractor.make_shareable(Farce.dedup(value)) when :local then Envelope::Local.new(value, self) when :make_shareable then Ractor.make_shareable(value) when :move then Envelope::Move.new(value, self) when :mutable then Mutable.new(value) when :raise then raise Ractor::IsolationError, "value is not Ractor-shareable: #{value.inspect}" when :shareable_copy then Ractor.make_shareable(value, copy: true) when :proxy then Proxy.new(value, register: @register) when :marshal then Envelope::Marshal.new(value, self) else raise ArgumentError, "invalid mode: #{mode.inspect}" end end |