<commit_before>require 'thread'

require 'timeout'
require 'listen/event/processor'

module Listen
  module Event
    class Loop
      include Listen::FSM

      class Error < RuntimeError
        class ThreadFailedToStart < Error; end
        class AlreadyStarted < Error; end
      end

      start_state :pre_start
      state :pre_start
      state :starting
      state :started
      state :stopped

      def initialize(config)
        @config = config
        @wait_thread = nil
        @reasons = ::Queue.new
        initialize_fsm
      end

      def wakeup_on_event
        if started? && @wait_thread&.alive?
          _wakeup(:event)
        end
      end

      def started?
        state == :started
      end

      MAX_STARTUP_SECONDS = 5.0

      def start
        # TODO: use a Fiber instead?
        transition! :starting do
          state == :pre_start or raise Error::AlreadyStarted
        end

        @wait_thread = Thread.new do
          _process_changes
        end

        Listen::Logger.debug("Waiting for processing to start...")

        wait_for_state(:started, MAX_STARTUP_SECONDS) or raise Error::ThreadFailedToStart, "thread didn't start in #{MAX_STARTUP_SECONDS} seconds (in state: #{state.inspect})"

        Listen::Logger.debug('Processing started.')
      end

      def pause
        # TODO: works?
        # fail NotImplementedError
      end

      def stop
        return if stopped?
        transition! :stopped

        if @wait_thread.alive?
          @wait_thread.join.kill
        end<commit_msg>issue #481: restore previous public behavior: exception name (NotStarted), and silently no-op on start if already started<commit_after>require 'thread'

require 'timeout'
require 'listen/event/processor'

module Listen
  module Event
    class Loop
      include Listen::FSM

      class Error < RuntimeError
        class NotStarted < Error; end
      end

      start_state :pre_start
      state :pre_start
      state :starting
      state :started
      state :stopped

      def initialize(config)
        @config = config
        @wait_thread = nil
        @reasons = ::Queue.new
        initialize_fsm
      end

      def wakeup_on_event
        if started? && @wait_thread&.alive?
          _wakeup(:event)
        end
      end

      def started?
        state == :started
      end

      MAX_STARTUP_SECONDS = 5.0

      def start
        # TODO: use a Fiber instead?
        return unless state == :pre_start

        transition! :starting

        @wait_thread = Thread.new do
          _process_changes
        end

        Listen::Logger.debug("Waiting for processing to start...")

        wait_for_state(:started, MAX_STARTUP_SECONDS) or
          raise Error::NotStarted, "thread didn't start in #{MAX_STARTUP_SECONDS} seconds (in state: #{state.inspect})"

        Listen::Logger.debug('Processing started.')
      end

      def pause
        # TODO: works?
        # fail NotImplementedError
      end

      def stop
        return if stopped?
        transition! :stopped

        if @wait_thread.alive?
          @wait_thread.join.kill
        end
{'fraction_non_alphanumeric': 0.07396733909702209, 'fraction_numerical': 0.002241434518091579, 'mean_word_length': 2.012536162005786, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 3, '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': '4592069', 'n_tokens_mistral': 906, 'n_tokens_neox': 856, 'n_words': 252}