Class: Farce::Atom
- Inherits:
-
Farce::Abstract::Atom
- Object
- Farce::Abstract::Atom
- Farce::Atom
- Includes:
- Shareable::Delegated
- Defined in:
- lib/farce/atom.rb,
lib/farce/integrations/psych.rb,
lib/farce/integrations/active_support/blank.rb
Overview
A Ractor-shareable atomic reference that strongly retains its current value and can hold non-shareable values.
Values are transferred according to the atom's default #mode, or an operation-specific mode where supported. Values returned by the atom are automatically unwrapped. Updates are serialized, and comparison operations compare the stored values without claiming or opening their envelopes when possible.
ActiveSupport Integration collapse
-
#blank? ⇒ Boolean
True if the atom's value is blank, false otherwise.
Methods included from Internal::ValueSerialization
Methods included from Internal::Copyable
Instance Method Summary collapse
-
#compare_and_set(expected, new_value, mode: nil, timeout: nil) ⇒ Boolean
Atomically replace the current value if it matches the expected value.
-
#compare_by_identity? ⇒ Boolean
Whether comparisons use object identity instead of equality.
-
#get(timeout: nil) { ... } ⇒ BasicObject?
Return the current value, waiting for any update in progress.
-
#initialize(value = nil, compare_by_identity: false, mode: :smart) ⇒ Atom
constructor
Valid modes are: *
:smart- Applies smart rules or a dedicated proxy. -
#mode ⇒ Symbol
The default mode used to transfer values between Ractors.
-
#store(new_value, mode: nil, timeout: nil) { ... } ⇒ BasicObject?
Store a new value, waiting for any update in progress.
-
#store_if_absent(mode: nil, timeout: nil) { ... } ⇒ BasicObject?
Compute and store a value if the current value is nil.
-
#swap(new_value, mode: nil, timeout: nil) { ... } ⇒ BasicObject?
Replace the current value and return the previous value.
-
#update(mode: nil, timeout: nil) {|current| ... } ⇒ BasicObject?
Atomically replace the current value with the result of a block.
-
#upsert(initial_value, mode: nil, timeout: nil) {|current| ... } ⇒ BasicObject?
Store an initial value if the current value is nil, otherwise replace it with the result of a block.
-
#value ⇒ BasicObject?
Return the current value without waiting for an update in progress.
-
#value=(new_value) ⇒ BasicObject?
Store a value using the default mode.
-
#wait_until(timeout: nil) {|value| ... } ⇒ BasicObject?
Wait until a block condition matches the current value.
-
#wait_until_changed(expected, timeout: nil) { ... } ⇒ BasicObject?
Wait until the current value no longer matches an expected value.
-
#wait_until_non_nil(timeout: nil) { ... } ⇒ BasicObject?
Wait until the current value is not nil.
Methods included from Shareable
Methods inherited from Farce::Abstract::Atom
#wait_until_match, #wait_until_value, #wait_while, #wait_while_match, #wait_while_value
Methods included from Internal::ValueSerialization
#as_extended_json, #to_bson, #to_bson_normalized_value, #to_cbor, #to_json, #to_msgpack
Methods included from Farce::Abstract::Value
Constructor Details
#initialize(value = nil, compare_by_identity: false, mode: :smart) ⇒ Atom
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 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.
47 48 49 50 51 52 53 54 55 56 |
# File 'lib/farce/atom.rb', line 47 def initialize(value = nil, compare_by_identity: false, mode: :smart) unless compare_by_identity == true || compare_by_identity == false raise ArgumentError, "compare_by_identity must be a boolean" end @manager = ModeManager.new(mode:) @compare_by_identity = compare_by_identity @atom = Internal::Atom.new(wrap_value(value), compare_by_identity: true) super() end |
Instance Method Details
#blank? ⇒ Boolean
This methods is only available if ActiveSupport has been loaded.
Returns true if the atom's value is blank, false otherwise.
47 48 49 50 |
# File 'lib/farce/integrations/active_support/blank.rb', line 47 def blank? current = internal_atom.value NIL_VALUE.equal?(current) || current.blank? end |
#compare_and_set(expected, new_value, mode: nil, timeout: nil) ⇒ Boolean
Atomically replace the current value if it matches the expected value. 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 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.
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/farce/atom.rb', line 166 def compare_and_set(expected, new_value, mode: nil, timeout: nil) check_frozen! expected = wrap_comparison(expected) matched = false @atom.update(timeout:) do |current| equal = values_equal?(current, expected) check_frozen! next current unless equal matched = true wrap_value(new_value, mode:) end matched end |
#compare_by_identity? ⇒ Boolean
Whether comparisons use object identity instead of equality.
87 |
# File 'lib/farce/atom.rb', line 87 def compare_by_identity? = @compare_by_identity |
#get(timeout: nil) { ... } ⇒ BasicObject?
Return the current value, waiting for any update in progress.
104 |
# File 'lib/farce/atom.rb', line 104 def get(timeout: nil, &) = unwrap_result(@atom.get(timeout:) { TIMED_OUT }, &) |
#mode ⇒ Symbol
The default mode used to transfer values between Ractors.
83 |
# File 'lib/farce/atom.rb', line 83 def mode = @manager.mode |
#store(new_value, mode: nil, timeout: nil) { ... } ⇒ BasicObject?
Store a new value, waiting for any update in progress. 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 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.
113 114 115 116 117 |
# File 'lib/farce/atom.rb', line 113 def store(new_value, mode: nil, timeout: nil, &) check_frozen! new_value = wrap_value(new_value, mode:) unwrap_result(@atom.store(new_value, timeout:) { TIMED_OUT }, &) end |
#store_if_absent(mode: nil, timeout: nil) { ... } ⇒ BasicObject?
Compute and store a value if the current value is nil. 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 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.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/farce/atom.rb', line 139 def store_if_absent(mode: nil, timeout: nil) raise LocalJumpError, "no block given" unless block_given? check_frozen! if timeout.nil? # A hit must still wait for an update in progress. current = @atom.get return unwrap_value(current) unless NIL_VALUE.equal?(current) end result = @atom.update(timeout:) do |current| next current unless NIL_VALUE.equal?(current) value = yield check_frozen! wrap_value(value, mode:) end unwrap_value(result) end |
#swap(new_value, mode: nil, timeout: nil) { ... } ⇒ BasicObject?
Replace the current value and return the previous value. 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 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.
126 127 128 129 130 |
# File 'lib/farce/atom.rb', line 126 def swap(new_value, mode: nil, timeout: nil, &) check_frozen! new_value = wrap_value(new_value, mode:) unwrap_result(@atom.swap(new_value, timeout:) { TIMED_OUT }, &) end |
#update(mode: nil, timeout: nil) {|current| ... } ⇒ BasicObject?
Atomically replace the current value with the result of a block. 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 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.
190 191 192 193 194 195 196 197 198 199 |
# File 'lib/farce/atom.rb', line 190 def update(mode: nil, timeout: nil) raise LocalJumpError, "no block given" unless block_given? check_frozen! result = @atom.update(timeout:) do |current| value = yield(unwrap_value(current)) check_frozen! wrap_value(value, mode:) end unwrap_value(result) end |
#upsert(initial_value, mode: nil, timeout: nil) {|current| ... } ⇒ BasicObject?
Store an initial value if the current value is nil, otherwise replace it with the result of a block. 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 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.
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/farce/atom.rb', line 210 def upsert(initial_value, mode: nil, timeout: nil) raise LocalJumpError, "no block given" unless block_given? check_frozen! result = @atom.update(timeout:) do |current| if NIL_VALUE.equal?(current) wrap_value(initial_value, mode:) else value = yield(unwrap_value(current)) check_frozen! wrap_value(value, mode:) end end unwrap_value(result) end |
#value ⇒ BasicObject?
Return the current value without waiting for an update in progress.
91 |
# File 'lib/farce/atom.rb', line 91 def value = unwrap_value(@atom.value) |
#value=(new_value) ⇒ BasicObject?
Store a value using the default mode.
96 97 98 |
# File 'lib/farce/atom.rb', line 96 def value=(new_value) store(new_value) end |
#wait_until(timeout: nil) {|value| ... } ⇒ BasicObject?
Wait until a block condition matches the current value. One timeout budget covers all checks and waits. The block is not interrupted.
227 228 229 230 231 |
# File 'lib/farce/atom.rb', line 227 def wait_until(timeout: nil) raise LocalJumpError, "no block given" unless block_given? result = Internal.wait_until(@atom, timeout:) { |current| yield(unwrap_value(current)) } unwrap_value(result) end |
#wait_until_changed(expected, timeout: nil) { ... } ⇒ BasicObject?
Wait until the current value no longer matches an expected value.
238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/farce/atom.rb', line 238 def wait_until_changed(expected, timeout: nil, &) expected = wrap_comparison(expected) deadline = Internal.timeout_deadline(timeout) while true current = @atom.get(timeout: Clock.remaining(deadline)) { TIMED_OUT } return unwrap_result(current, &) if TIMED_OUT.equal?(current) return unwrap_value(current) unless values_equal?(current, expected) result = @atom.wait_until_changed(current, timeout: Clock.remaining(deadline)) { TIMED_OUT } return unwrap_result(result, &) if TIMED_OUT.equal?(result) end end |
#wait_until_non_nil(timeout: nil) { ... } ⇒ BasicObject?
Wait until the current value is not nil.
256 |
# File 'lib/farce/atom.rb', line 256 def wait_until_non_nil(timeout: nil, &) = wait_until_changed(nil, timeout:, &) |