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: :copy) ⇒ Atom
constructor
Valid modes are: *
:copy- The value will be copied between Ractors. -
#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_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, #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
Methods included from Farce::Abstract::Value
Constructor Details
#initialize(value = nil, compare_by_identity: false, mode: :copy) ⇒ Atom
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 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: :copy) 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:
: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 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.
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/farce/atom.rb', line 160 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:
: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 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:
: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 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 |
# 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! 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:
: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 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:
: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 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.
184 185 186 187 188 189 190 191 192 193 |
# File 'lib/farce/atom.rb', line 184 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:
: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 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.
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/farce/atom.rb', line 204 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_changed(expected, timeout: nil) { ... } ⇒ BasicObject?
Wait until the current value no longer matches an expected value.
225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/farce/atom.rb', line 225 def wait_until_changed(expected, timeout: nil, &) expected = wrap_comparison(expected) deadline = Internal.timeout_deadline(timeout) while true current = @atom.get(timeout: Internal.remaining_timeout(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: Internal.remaining_timeout(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.
243 |
# File 'lib/farce/atom.rb', line 243 def wait_until_non_nil(timeout: nil, &) = wait_until_changed(nil, timeout:, &) |