Class: Farce::Vector
- Inherits:
-
Abstract::Vector
- Object
- Abstract::Collection
- Abstract::Vector
- Farce::Vector
- Includes:
- Shareable::Delegated
- Defined in:
- lib/farce/vector.rb
Overview
A Ractor-shareable vector with transfer modes for mutable values. Values stored through this vector's mode manager are automatically unwrapped.
Instance Method Summary collapse
-
#[](index, length = UNDEFINED) ⇒ BasicObject, ...
Read an index without waiting for atomic-update access, or return a slice snapshot.
-
#[]=(index, value, replacement = UNDEFINED) ⇒ BasicObject
Store a value or replace a slice atomically, growing the vector if necessary.
-
#append(*values, mode: nil, timeout: nil) ⇒ self, false
Append values using the same options as #push.
-
#compare_and_set(index, expected, replacement, mode: nil, timeout: nil) ⇒ Boolean
Replace an existing index only if its value matches the expected value.
-
#compare_by_identity? ⇒ Boolean
Whether values are compared by identity.
-
#get(index, timeout: nil) ⇒ BasicObject?
Read an index after acquiring atomic-update access.
-
#initialize(source = nil, mode: :smart, compare_by_identity: false) ⇒ Vector
constructor
Valid modes are: *
:smart- Applies smart rules or a dedicated proxy. -
#mode ⇒ Symbol
The default transfer mode.
-
#pop(count = UNDEFINED, timeout: nil) ⇒ BasicObject, ...
Remove and return the last value, or a Vector of the last count values, atomically.
-
#push(value = UNDEFINED, *rest, mode: nil, timeout: nil) ⇒ self, false
Append values atomically.
-
#shareable_values? ⇒ Boolean
Whether stored values must be Ractor-shareable.
-
#store(index, value, mode: nil, timeout: nil) ⇒ BasicObject, false
Store a value after acquiring atomic-update access.
-
#store_if_absent(index, mode: nil, timeout: nil) { ... } ⇒ BasicObject?
Compute and store a value only when the index is absent or contains nil.
-
#swap(index, replacement, mode: nil, timeout: nil) ⇒ BasicObject?
Replace an index and return its previous value, growing the vector if necessary.
-
#update(index, mode: nil, timeout: nil) {|value| ... } ⇒ BasicObject?
Atomically replace an index with the block result, growing the vector if necessary.
-
#upsert(index, initial, mode: nil, timeout: nil) {|value| ... } ⇒ BasicObject?
Store initial for an absent or nil index, otherwise replace it with the block result.
-
#wait_until(index, timeout: nil) {|value| ... } ⇒ BasicObject?
Wait until a block condition matches the value at an index.
-
#wait_until_changed(index, expected, timeout: nil) ⇒ BasicObject?
Wait until an index no longer matches expected.
-
#wait_until_non_nil(index, timeout: nil) ⇒ BasicObject?
Wait until an index contains a non-nil value.
Methods included from Shareable
Methods inherited from Abstract::Vector
#&, #*, #+, #-, #<<, #<=>, #as_extended_json, #as_json, #assoc, #at, #bsearch, #bsearch_index, #bson_type, #chunk, #chunk_while, #clear, #combination, #compact, #compact!, #compact_blank, #concat, #deconstruct, #deep_dup, #delete, #delete_at, #delete_if, #difference, #dig, #drop, #drop_while, #each, #each_cons, #each_index, #each_slice, #entries, #excluding, #fetch, #fetch_values, #fifth, #fill, #filter_map, #first, #flat_map, #flatten, #flatten!, #forty_two, #fourth, #from, #grep, #grep_v, #group_by, #in_groups, #in_groups_of, #in_order_of, #include?, #including, #index, #inquiry, #insert, #intersect?, #intersection, #keep_if, #last, #map, #map!, #max, #max_by, #maximum, #min, #min_by, #minimum, #minmax, #minmax_by, #pack, #partition, #permutation, #pick, #pluck, #product, #rassoc, #reject, #reject!, #repeated_combination, #repeated_permutation, #replace, #reverse, #reverse!, #reverse_each, #rfind, #rindex, #rotate, #rotate!, #sample, #second, #second_to_last, #select, #select!, #shift, #shuffle, #shuffle!, #size, #slice, #slice!, #slice_after, #slice_before, #slice_when, #sort, #sort!, #sort_by, #sort_by!, #split, #take, #take_while, #third, #third_to_last, #to, #to_a, #to_ary, #to_bson, #to_bson_normalized_value, #to_cbor, #to_fs, #to_h, #to_json, #to_msgpack, #to_param, #to_query, #to_sentence, #to_xml, #transpose, #union, #uniq, #uniq!, #unshift, #values_at, #wait_until_match, #wait_until_value, #wait_while, #wait_while_match, #wait_while_value, #zip, #|
Methods inherited from Abstract::Collection
#<<, [], #clear, #count, #each, #empty?, #filter, #include?, #join, #length, #member?, #reject, #select, #size, #to_a, #to_s
Methods included from Internal::Copyable
Constructor Details
#initialize(source = nil, mode: :smart, compare_by_identity: false) ⇒ Vector
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.
27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/farce/vector.rb', line 27 def initialize(source = nil, mode: :smart, compare_by_identity: false) source = normalize_vector_source(source) raise TypeError, "source must be an Array" unless source.nil? || Integer === source || source.is_a?(Array) @compare_by_identity = compare_by_identity ? true : false @manager = ModeManager.new(mode:) if source && !(Integer === source) source = source.map { @manager.wrap(it) } raise TypeError, "source must be an Array" unless source.nil? || source.is_a?(Array) end @vector = Internal::Vector.new_for_modes(source) super() end |
Instance Method Details
#[](index, length = UNDEFINED) ⇒ BasicObject, ...
Read an index without waiting for atomic-update access, or return a slice snapshot.
70 71 72 73 |
# File 'lib/farce/vector.rb', line 70 def [](index, length = UNDEFINED) return @manager.unwrap(@vector[index]) if length.equal?(UNDEFINED) && !index.is_a?(Range) slice(index, length) end |
#[]=(index, value, replacement = UNDEFINED) ⇒ BasicObject
Store a value or replace a slice atomically, growing the vector if necessary. Slice replacements accept a Vector, Array-like object, or one scalar value.
76 77 78 79 80 81 82 83 84 |
# File 'lib/farce/vector.rb', line 76 def []=(index, value, replacement = UNDEFINED) if replacement.equal?(UNDEFINED) && !index.is_a?(Range) check_frozen! @vector[index] = @manager.wrap(value) value else assign_vector_slice(index, value, replacement) end end |
#append(*values, mode: nil, timeout: nil) ⇒ self, false
Append values using the same options as #push.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/farce/vector.rb', line 19 class Vector < Farce::Abstract::Vector include Shareable::Delegated # @!macro modes # @param source [Array, Integer, nil] Initial values or a size filled with nil. The source array is not retained. # @param mode [Symbol] The default value transfer mode. # @param compare_by_identity [Boolean] Whether values are compared by identity. # @raise [ArgumentError] If the size is negative. def initialize(source = nil, mode: :smart, compare_by_identity: false) source = normalize_vector_source(source) raise TypeError, "source must be an Array" unless source.nil? || Integer === source || source.is_a?(Array) @compare_by_identity = compare_by_identity ? true : false @manager = ModeManager.new(mode:) if source && !(Integer === source) source = source.map { @manager.wrap(it) } raise TypeError, "source must be an Array" unless source.nil? || source.is_a?(Array) end @vector = Internal::Vector.new_for_modes(source) super() end # @api private def transaction_wrapper(transaction) = Transaction::Vector.new(transaction, self, @vector, manager: @manager) # @return [Symbol] The default transfer mode. def mode = @manager.mode # @api private def marshal_dump entries = @vector.snapshot.map { Internal::MarshalSupport.value(it) } [1, entries, @manager, compare_by_identity?, frozen?] end # @api private def marshal_load(data) entries, @manager, identity, frozen = Internal::MarshalSupport.payload(data, 4) @compare_by_identity = identity @vector = Internal::Vector.new_for_modes(nil) publish_shareable entries.each { @vector.push(Internal::MarshalSupport.restore_value(it)) } Internal::MarshalSupport.freeze(self, frozen) end # (see Farce::Abstract::Vector#compare_by_identity?) def compare_by_identity? = @compare_by_identity # (see Farce::Abstract::Vector#shareable_values?) def shareable_values? = true # (see Farce::Abstract::Vector#[]) def [](index, length = UNDEFINED) return @manager.unwrap(@vector[index]) if length.equal?(UNDEFINED) && !index.is_a?(Range) slice(index, length) end # (see Farce::Abstract::Vector#[]=) def []=(index, value, replacement = UNDEFINED) if replacement.equal?(UNDEFINED) && !index.is_a?(Range) check_frozen! @vector[index] = @manager.wrap(value) value else assign_vector_slice(index, value, replacement) end end # (see Farce::Abstract::Vector#get) def get(index, timeout: nil) = @manager.unwrap(@vector.get(index, timeout:)) # (see Farce::Abstract::Vector#store) # @param mode [Symbol, nil] The transfer mode, or nil to use the default. def store(index, value, mode: nil, timeout: nil) check_frozen! @manager.unwrap(@vector.store(index, @manager.wrap(value, mode:), timeout:)) end # (see Farce::Abstract::Vector#push) # @param mode [Symbol, nil] The transfer mode, or nil to use the default. def push(value = UNDEFINED, *rest, mode: nil, timeout: nil) if rest.empty? && !value.equal?(UNDEFINED) check_frozen! stored = mode.nil? ? @manager.wrap(value) : @manager.wrap(value, mode:) completed = timeout.nil? ? @vector.push(stored) : @vector.push(stored, timeout:) return completed ? self : false end push_values(value.equal?(UNDEFINED) ? rest : [value, *rest], mode:, timeout:) end # (see Farce::Abstract::Vector#pop) def pop(count = UNDEFINED, timeout: nil) return @manager.unwrap(timeout.nil? ? @vector.pop : @vector.pop(timeout:)) if count.equal?(UNDEFINED) pop_values(count, timeout:) end # (see Farce::Abstract::Vector#swap) # @!macro modes # @param mode [Symbol, nil] The replacement's transfer mode, or nil to use the default. def swap(index, replacement, mode: nil, timeout: nil) check_frozen! @manager.unwrap(@vector.swap(index, @manager.wrap(replacement, mode:), timeout:)) end # (see Farce::Abstract::Vector#store_if_absent) # @param mode [Symbol, nil] The result's transfer mode, or nil to use the default. def store_if_absent(index, mode: nil, timeout: nil) raise LocalJumpError, "no block given" unless block_given? check_frozen! @manager.unwrap(@vector.store_if_absent(index, timeout:) do value = yield check_frozen! @manager.wrap(value, mode:) end) end # (see Farce::Abstract::Vector#update) # @!macro modes # @param mode [Symbol, nil] The result's transfer mode, or nil to use the default. def update(index, mode: nil, timeout: nil) raise LocalJumpError, "no block given" unless block_given? check_frozen! @manager.unwrap(@vector.update(index, timeout:) do |current| value = yield(@manager.unwrap(current)) check_frozen! @manager.wrap(value, mode:) end) end # (see Farce::Abstract::Vector#upsert) # @!macro modes # @param mode [Symbol, nil] The value transfer mode, or nil to use the default. def upsert(index, initial, mode: nil, timeout: nil) raise LocalJumpError, "no block given" unless block_given? check_frozen! @manager.unwrap(@vector.update(index, timeout:) do |current| value = if nil.equal?(current) initial else yielded = yield(@manager.unwrap(current)) check_frozen! yielded end @manager.wrap(value, mode:) end) end # (see Farce::Abstract::Vector#compare_and_set) # @!macro modes # @param mode [Symbol, nil] The replacement's transfer mode, or nil to use the default. def compare_and_set(index, expected, replacement, mode: nil, timeout: nil) check_frozen! deadline = timeout_deadline(timeout) expected = wrap_comparison(expected) wrapped = false loop do current = @vector[index] equal = values_equal?(current, expected) check_frozen! return false unless equal unless wrapped replacement = @manager.wrap(replacement, mode:) wrapped = true end return true if @vector.compare_and_set(index, current, replacement, timeout: Clock.remaining(deadline)) return false if nil.equal?(current) || (deadline && Clock.now >= deadline) end end # (see Farce::Abstract::Vector#wait_until) def wait_until(index, timeout: nil) raise LocalJumpError, "no block given" unless block_given? result = Internal.wait_until(@vector, index, timeout:) { |current| yield(@manager.unwrap(current)) } @manager.unwrap(result) end # (see Farce::Abstract::Vector#wait_until_changed) def wait_until_changed(index, expected, timeout: nil) deadline = timeout_deadline(timeout) expected = wrap_comparison(expected) loop do current = @vector[index] return @manager.unwrap(current) unless values_equal?(current, expected) return if deadline && Clock.now >= deadline @vector.wait_until_changed(index, current, timeout: Clock.remaining(deadline)) end end # (see Farce::Abstract::Vector#wait_until_non_nil) def wait_until_non_nil(index, timeout: nil) = @manager.unwrap(@vector.wait_until_non_nil(index, timeout:)) protected def logical_value(value) = @manager.unwrap(value) def deletion_comparison(value) = wrap_comparison(value) def deletion_match?(stored, expected) = values_equal?(stored, expected) def build_derived_vector(values) self.class.allocate.initialize_derived_vector(values, @manager, @compare_by_identity) end def initialize_derived_vector(values, manager, compare_by_identity) @manager = manager @compare_by_identity = compare_by_identity @vector = Internal::Vector.new(values, compare_by_identity: true) publish_shareable end attr_reader :manager def reusable_operand_snapshot(other) return unless other.is_a?(Farce::Vector) return unless other.manager.equal?(@manager) other.internal_vector.snapshot end private def inspect_value(inspector, value) = super(inspector, value, @manager) def derived_storage(value) = @manager.wrap(value) def prepare_vector_value(value, mode: nil) = @manager.wrap(value, mode:) # Expanded descendants have no independent envelope to reuse. Copy them rather than # moving them out of a retained parent Array or another sequence's storage. def prepare_sequence_value(value) @manager.wrap(value, mode: mode == :move ? :copy : nil) end def freeze_backend = @vector def wrap_comparison(value) = @manager.wrap(value, mode: compare_by_identity? ? :local : :copy) def values_equal?(left, right) = @manager.same_value?(left, right, identity: compare_by_identity?) def timeout_deadline(timeout) return if timeout.nil? timeout = Float(timeout) raise ArgumentError, "timeout must be finite and non-negative" unless timeout.finite? && !timeout.negative? Clock.now + timeout end end |
#compare_and_set(index, expected, replacement, mode: nil, timeout: nil) ⇒ Boolean
Replace an existing index only if its value matches the expected value. This never grows the vector. Matching uses the configured comparison mode. 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.
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/farce/vector.rb', line 168 def compare_and_set(index, expected, replacement, mode: nil, timeout: nil) check_frozen! deadline = timeout_deadline(timeout) expected = wrap_comparison(expected) wrapped = false loop do current = @vector[index] equal = values_equal?(current, expected) check_frozen! return false unless equal unless wrapped replacement = @manager.wrap(replacement, mode:) wrapped = true end return true if @vector.compare_and_set(index, current, replacement, timeout: Clock.remaining(deadline)) return false if nil.equal?(current) || (deadline && Clock.now >= deadline) end end |
#compare_by_identity? ⇒ Boolean
Returns Whether values are compared by identity.
64 |
# File 'lib/farce/vector.rb', line 64 def compare_by_identity? = @compare_by_identity |
#get(index, timeout: nil) ⇒ BasicObject?
Read an index after acquiring atomic-update access.
87 |
# File 'lib/farce/vector.rb', line 87 def get(index, timeout: nil) = @manager.unwrap(@vector.get(index, timeout:)) |
#mode ⇒ Symbol
Returns The default transfer mode.
45 |
# File 'lib/farce/vector.rb', line 45 def mode = @manager.mode |
#pop(count = UNDEFINED, timeout: nil) ⇒ BasicObject, ...
Remove and return the last value, or a Vector of the last count values, atomically. This does not wait for an empty vector to become nonempty.
109 110 111 112 |
# File 'lib/farce/vector.rb', line 109 def pop(count = UNDEFINED, timeout: nil) return @manager.unwrap(timeout.nil? ? @vector.pop : @vector.pop(timeout:)) if count.equal?(UNDEFINED) pop_values(count, timeout:) end |
#push(value = UNDEFINED, *rest, mode: nil, timeout: nil) ⇒ self, false
Append values atomically. Calls with one value use the scalar backend path.
98 99 100 101 102 103 104 105 106 |
# File 'lib/farce/vector.rb', line 98 def push(value = UNDEFINED, *rest, mode: nil, timeout: nil) if rest.empty? && !value.equal?(UNDEFINED) check_frozen! stored = mode.nil? ? @manager.wrap(value) : @manager.wrap(value, mode:) completed = timeout.nil? ? @vector.push(stored) : @vector.push(stored, timeout:) return completed ? self : false end push_values(value.equal?(UNDEFINED) ? rest : [value, *rest], mode:, timeout:) end |
#shareable_values? ⇒ Boolean
Returns Whether stored values must be Ractor-shareable.
67 |
# File 'lib/farce/vector.rb', line 67 def shareable_values? = true |
#store(index, value, mode: nil, timeout: nil) ⇒ BasicObject, false
Store a value after acquiring atomic-update access.
91 92 93 94 |
# File 'lib/farce/vector.rb', line 91 def store(index, value, mode: nil, timeout: nil) check_frozen! @manager.unwrap(@vector.store(index, @manager.wrap(value, mode:), timeout:)) end |
#store_if_absent(index, mode: nil, timeout: nil) { ... } ⇒ BasicObject?
Compute and store a value only when the index is absent or contains nil.
124 125 126 127 128 129 130 131 132 |
# File 'lib/farce/vector.rb', line 124 def store_if_absent(index, mode: nil, timeout: nil) raise LocalJumpError, "no block given" unless block_given? check_frozen! @manager.unwrap(@vector.store_if_absent(index, timeout:) do value = yield check_frozen! @manager.wrap(value, mode:) end) end |
#swap(index, replacement, mode: nil, timeout: nil) ⇒ BasicObject?
Replace an index and return its previous value, growing the vector if necessary. 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.
117 118 119 120 |
# File 'lib/farce/vector.rb', line 117 def swap(index, replacement, mode: nil, timeout: nil) check_frozen! @manager.unwrap(@vector.swap(index, @manager.wrap(replacement, mode:), timeout:)) end |
#update(index, mode: nil, timeout: nil) {|value| ... } ⇒ BasicObject?
Atomically replace an index with the block result, growing the vector if necessary. 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.
137 138 139 140 141 142 143 144 145 |
# File 'lib/farce/vector.rb', line 137 def update(index, mode: nil, timeout: nil) raise LocalJumpError, "no block given" unless block_given? check_frozen! @manager.unwrap(@vector.update(index, timeout:) do |current| value = yield(@manager.unwrap(current)) check_frozen! @manager.wrap(value, mode:) end) end |
#upsert(index, initial, mode: nil, timeout: nil) {|value| ... } ⇒ BasicObject?
Store initial for an absent or nil index, otherwise replace it with the block result. 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.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/farce/vector.rb', line 150 def upsert(index, initial, mode: nil, timeout: nil) raise LocalJumpError, "no block given" unless block_given? check_frozen! @manager.unwrap(@vector.update(index, timeout:) do |current| value = if nil.equal?(current) initial else yielded = yield(@manager.unwrap(current)) check_frozen! yielded end @manager.wrap(value, mode:) end) end |
#wait_until(index, timeout: nil) {|value| ... } ⇒ BasicObject?
Wait until a block condition matches the value at an index. Absent indexes are observed as nil. One timeout budget covers all checks and waits. The block is not interrupted.
188 189 190 191 192 |
# File 'lib/farce/vector.rb', line 188 def wait_until(index, timeout: nil) raise LocalJumpError, "no block given" unless block_given? result = Internal.wait_until(@vector, index, timeout:) { |current| yield(@manager.unwrap(current)) } @manager.unwrap(result) end |
#wait_until_changed(index, expected, timeout: nil) ⇒ BasicObject?
Wait until an index no longer matches expected. Absent indexes are observed as nil.
195 196 197 198 199 200 201 202 203 204 |
# File 'lib/farce/vector.rb', line 195 def wait_until_changed(index, expected, timeout: nil) deadline = timeout_deadline(timeout) expected = wrap_comparison(expected) loop do current = @vector[index] return @manager.unwrap(current) unless values_equal?(current, expected) return if deadline && Clock.now >= deadline @vector.wait_until_changed(index, current, timeout: Clock.remaining(deadline)) end end |
#wait_until_non_nil(index, timeout: nil) ⇒ BasicObject?
Wait until an index contains a non-nil value.
207 |
# File 'lib/farce/vector.rb', line 207 def wait_until_non_nil(index, timeout: nil) = @manager.unwrap(@vector.wait_until_non_nil(index, timeout:)) |