87 lines
3.0 KiB
Ruby
87 lines
3.0 KiB
Ruby
|
require 'concurrent/atomic/atomic_reference'
|
||
|
|
||
|
module Concurrent
|
||
|
|
||
|
# A fixed size array with volatile (synchronized, thread safe) getters/setters.
|
||
|
# Mixes in Ruby's `Enumerable` module for enhanced search, sort, and traversal.
|
||
|
#
|
||
|
# @example
|
||
|
# tuple = Concurrent::Tuple.new(16)
|
||
|
#
|
||
|
# tuple.set(0, :foo) #=> :foo | volatile write
|
||
|
# tuple.get(0) #=> :foo | volatile read
|
||
|
# tuple.compare_and_set(0, :foo, :bar) #=> true | strong CAS
|
||
|
# tuple.cas(0, :foo, :baz) #=> false | strong CAS
|
||
|
# tuple.get(0) #=> :bar | volatile read
|
||
|
#
|
||
|
# @see https://en.wikipedia.org/wiki/Tuple Tuple entry at Wikipedia
|
||
|
# @see http://www.erlang.org/doc/reference_manual/data_types.html#id70396 Erlang Tuple
|
||
|
# @see http://ruby-doc.org/core-2.2.2/Enumerable.html Enumerable
|
||
|
class Tuple
|
||
|
include Enumerable
|
||
|
|
||
|
# The (fixed) size of the tuple.
|
||
|
attr_reader :size
|
||
|
|
||
|
# @!visibility private
|
||
|
Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : ::Array
|
||
|
private_constant :Tuple
|
||
|
|
||
|
# Create a new tuple of the given size.
|
||
|
#
|
||
|
# @param [Integer] size the number of elements in the tuple
|
||
|
def initialize(size)
|
||
|
@size = size
|
||
|
@tuple = tuple = Tuple.new(size)
|
||
|
i = 0
|
||
|
while i < size
|
||
|
tuple[i] = Concurrent::AtomicReference.new
|
||
|
i += 1
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Get the value of the element at the given index.
|
||
|
#
|
||
|
# @param [Integer] i the index from which to retrieve the value
|
||
|
# @return [Object] the value at the given index or nil if the index is out of bounds
|
||
|
def get(i)
|
||
|
return nil if i >= @size || i < 0
|
||
|
@tuple[i].get
|
||
|
end
|
||
|
alias_method :volatile_get, :get
|
||
|
|
||
|
# Set the element at the given index to the given value
|
||
|
#
|
||
|
# @param [Integer] i the index for the element to set
|
||
|
# @param [Object] value the value to set at the given index
|
||
|
#
|
||
|
# @return [Object] the new value of the element at the given index or nil if the index is out of bounds
|
||
|
def set(i, value)
|
||
|
return nil if i >= @size || i < 0
|
||
|
@tuple[i].set(value)
|
||
|
end
|
||
|
alias_method :volatile_set, :set
|
||
|
|
||
|
# Set the value at the given index to the new value if and only if the current
|
||
|
# value matches the given old value.
|
||
|
#
|
||
|
# @param [Integer] i the index for the element to set
|
||
|
# @param [Object] old_value the value to compare against the current value
|
||
|
# @param [Object] new_value the value to set at the given index
|
||
|
#
|
||
|
# @return [Boolean] true if the value at the given element was set else false
|
||
|
def compare_and_set(i, old_value, new_value)
|
||
|
return false if i >= @size || i < 0
|
||
|
@tuple[i].compare_and_set(old_value, new_value)
|
||
|
end
|
||
|
alias_method :cas, :compare_and_set
|
||
|
|
||
|
# Calls the given block once for each element in self, passing that element as a parameter.
|
||
|
#
|
||
|
# @yieldparam [Object] ref the `Concurrent::AtomicReference` object at the current index
|
||
|
def each
|
||
|
@tuple.each {|ref| yield ref.get}
|
||
|
end
|
||
|
end
|
||
|
end
|