<commit_before>require 'atomic'

module Spotify
  # The Reaper!
  #
  # Garbage collection may happen in a separate thread, and if we are
  # unfortunate enough the main thread will hold the API lock around Spotify,
  # which makes it impossible for the GC thread to obtain the lock to release
  # pointers.
  #
  # When garbage collection happens in a separate thread, it’s possible that
  # the main threads will not run until garbage collection is finished. If the
  # GC thread deadlocks, the entire application deadlocks.
  #
  # The idea is to locklessly pass pointers that need releasing to a queue. The
  # worker working the queue should be safe to acquire the API lock because
  # nobody is waiting for the working queue to ever finish.
  class Reaper
    # Freeze to prevent against modification.
    EMPTY = [].freeze

    # Time to sleep between each reaping.
    IDLE_TIME = 1

    # The underlying Reaper thread.
    attr_reader :reaper_thread

    class << self
      # @return [Reaper] The Reaper.
      attr_accessor :instance

      # @return [Boolean] true if Reaper should terminate at exit.
      attr_accessor :terminate_at_exit
    end

    def initialize(idle_time = IDLE_TIME)
      @run = true
      @queue = Atomic.new(EMPTY)

      @reaper_thread = Thread.new do
        begin
          while @run
            pointers = @queue.swap(EMPTY)
            pointers.each(&:free)
            sleep(*idle_time) # support sleeping forever
          end
        ensure
          Thread.current[:exception] = exception = $!
          Spotify.log "Spotify::Reaper WAS KILLED: #{exception.inspect}!" if exception
        end
      end
    end

    # Mark a pointer for release. Thread-safe, uses no locks.
    #
    # @note This is called from the GC thread, and as such it is very important
    # that this never waits for a lock held by the main Ruby thread, or we will
    # get deadlocks here.
    #
    # @param [#free] pointer
    def mark(pointer)
      # Possible race-condition here. Don't really care.
      if reaper_thread.alive?
        Spotify.log "Spotify::Reaper#mark(#{pointer.inspect})"

        @queue.update do |queue|
          # this needs to be able to run without side-effects as many
          # times as may be needed
          [pointer].unshift(*queue)
        end

        reaper_thread.wakeup
      else
        Spotify.log "Spotify::Reaper is dead. Cannot mark (#{pointer.inspect})."
      end
    rescue => e
      # Finalizers do not show raised errors.
      Spotify.log "Spotify::Reaper#mark FAILED: #{e} #{e.message}."
      raise
    end

    # Terminate the Reaper. Will wait until the Reaper exits.
    def terminate(wait_time = IDLE_TIME)
      if reaper_thread.alive?
        Spotify.log "Spotify::Reaper terminating."
        @run = false
        reaper_thread.run
        unless reaper_thread.join(wait_time)
          # at_exit hooks don't show errors.
          Spotify.log "Spotify::Reaper did not terminate within #{wait_time}."
          raise Spotify::Error, "Spotify::Reaper did not terminate within #{wait_time}."
        end
      end
    end

    @instance = new<commit_msg>Mark wait time as explicitly in seconds<commit_after>require 'atomic'

module Spotify
  # The Reaper!
  #
  # Garbage collection may happen in a separate thread, and if we are
  # unfortunate enough the main thread will hold the API lock around Spotify,
  # which makes it impossible for the GC thread to obtain the lock to release
  # pointers.
  #
  # When garbage collection happens in a separate thread, it’s possible that
  # the main threads will not run until garbage collection is finished. If the
  # GC thread deadlocks, the entire application deadlocks.
  #
  # The idea is to locklessly pass pointers that need releasing to a queue. The
  # worker working the queue should be safe to acquire the API lock because
  # nobody is waiting for the working queue to ever finish.
  class Reaper
    # Freeze to prevent against modification.
    EMPTY = [].freeze

    # Time to sleep between each reaping.
    IDLE_TIME = 1

    # The underlying Reaper thread.
    attr_reader :reaper_thread

    class << self
      # @return [Reaper] The Reaper.
      attr_accessor :instance

      # @return [Boolean] true if Reaper should terminate at exit.
      attr_accessor :terminate_at_exit
    end

    def initialize(idle_time = IDLE_TIME)
      @run = true
      @queue = Atomic.new(EMPTY)

      @reaper_thread = Thread.new do
        begin
          while @run
            pointers = @queue.swap(EMPTY)
            pointers.each(&:free)
            sleep(*idle_time) # support sleeping forever
          end
        ensure
          Thread.current[:exception] = exception = $!
          Spotify.log "Spotify::Reaper WAS KILLED: #{exception.inspect}!" if exception
        end
      end
    end

    # Mark a pointer for release. Thread-safe, uses no locks.
    #
    # @note This is called from the GC thread, and as such it is very important
    # that this never waits for a lock held by the main Ruby thread, or we will
    # get deadlocks here.
    #
    # @param [#free] pointer
    def mark(pointer)
      # Possible race-condition here. Don't really care.
      if reaper_thread.alive?
        Spotify.log "Spotify::Reaper#mark(#{pointer.inspect})"

        @queue.update do |queue|
          # this needs to be able to run without side-effects as many
          # times as may be needed
          [pointer].unshift(*queue)
        end

        reaper_thread.wakeup
      else
        Spotify.log "Spotify::Reaper is dead. Cannot mark (#{pointer.inspect})."
      end
    rescue => e
      # Finalizers do not show raised errors.
      Spotify.log "Spotify::Reaper#mark FAILED: #{e} #{e.message}."
      raise
    end

    # Terminate the Reaper. Will wait until the Reaper exits.
    def terminate(wait_time = IDLE_TIME)
      if reaper_thread.alive?
        Spotify.log "Spotify::Reaper terminating."
        @run = false
        reaper_thread.run
        unless reaper_thread.join(wait_time)
          # at_exit hooks don't show errors.
          Spotify.log "Spotify::Reaper did not terminate within #{wait_time}."
          raise Spotify::Error, "Spotify::Reaper did not terminate within #{wait_time} seconds."
        end
      end
    end

    @instance = new
{'fraction_non_alphanumeric': 0.07405063291139241, 'fraction_numerical': 0.00031645569620253165, 'mean_word_length': 2.424160346695558, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3267716', 'n_tokens_mistral': 1832, 'n_tokens_neox': 1763, 'n_words': 690}