Class: Farce::ModeManager
- Inherits:
-
Object
- Object
- Farce::ModeManager
- Includes:
- Internal::Copyable, Shareable::Immutable
- Defined in:
- lib/farce/mode_manager.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: :copy)
@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:
:copy- The value will be copied between Ractors. This is the default mode.: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.
Instance Attribute Summary collapse
-
#mode ⇒ Symbol
readonly
The default mode to use when wrapping values.
Instance Method Summary collapse
-
#initialize(mode: :copy, register: nil) ⇒ ModeManager
constructor
Valid modes are: *
:copy- The value will be copied between Ractors. -
#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: :copy, register: nil) ⇒ ModeManager
Valid modes are:
:copy- The value will be copied between Ractors. This is the default mode.: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.
61 62 63 64 65 66 |
# File 'lib/farce/mode_manager.rb', line 61 def initialize(mode: :copy, 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.
56 57 58 |
# File 'lib/farce/mode_manager.rb', line 56 def mode @mode end |
Instance Method Details
#managed_envelope?(value) ⇒ Boolean
Returns Whether the value is an envelope managed by this mode manager.
140 |
# File 'lib/farce/mode_manager.rb', line 140 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.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/farce/mode_manager.rb', line 120 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.
109 110 111 112 |
# File 'lib/farce/mode_manager.rb', line 109 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:
:copy- The value will be copied between Ractors. This is the default mode.: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.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/farce/mode_manager.rb', line 87 def wrap(value, mode: nil) return value if Ractor.shareable?(value) case mode || 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) else raise ArgumentError, "invalid mode: #{mode.inspect}" end end |