# frozen_string_literal: true

module I18n::Tasks::Concurrent
  # A thread-safe memoized value.
  # The given computation is guaranteed to be invoked at most once.
  # @since 0.9.25
  class CachedValue
    NULL = Object.new

    # @param [Proc] computation The computation that returns the value to cache.
    def initialize(&computation)
      @computation = computation
      @mutex = Mutex.new
      @result = NULL
    end

    # @return [Object] Result of the computation.
    def get
      return get_result_volatile unless get_result_volatile == NULL

      @mutex.synchronize do
        next unless get_result_volatile == NULL

        set_result_volatile @computation.call
        @computation = nil
      end
      get_result_volatile
    end

    private

    # Ruby instance variable volatility is currently unspecified:
    # https://bugs.ruby-lang.org/issues/11539
    #
    # Below are the implementations for major ruby engines, based on concurrent-ruby.
    # rubocop:disable Lint/DuplicateMethods,Naming/AccessorMethodName
    case RUBY_ENGINE
    when "rbx"
      def get_result_volatile
        Rubinius.memory_barrier
        @result
      end

      def set_result_volatile(value)
        @result = value
        Rubinius.memory_barrier
      end
    else
      def get_result_volatile
        @result
      end

      def set_result_volatile(value)
        @result = value
      end
    end
    # rubocop:enable Lint/DuplicateMethods,Naming/AccessorMethodName
  end
end
