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