<commit_before>require 'thread'

module Adhearsion
  ##
  # Encapsulates call-related data and behavior.
  #
  class Call

    include HasGuardedHandlers

    attr_accessor :offer, :client, :end_reason, :commands, :variables

    delegate :[], :[]=, :to => :variables

    def initialize(offer = nil)
      register_initial_handlers

      @tag_mutex        = Mutex.new
      @tags             = []
      @end_reason_mutex = Mutex.new
      @commands         = CommandRegistry.new
      @variables        = SynchronizedHash.new

      self << offer if offer
    end

    def id
      offer.call_id
    end

    def tags
      @tag_mutex.synchronize { @tags.clone }
    end

    # This may still be a symbol, but no longer requires the tag to be a symbol although beware
    # that using a symbol would create a memory leak if used improperly
    # @param [String, Symbol] label String or Symbol with which to tag this call
    def tag(label)
      raise ArgumentError, "Tag must be a String or Symbol" unless [String, Symbol].include?(label.class)
      @tag_mutex.synchronize { @tags << label }
    end

    def remove_tag(symbol)
      @tag_mutex.synchronize do
        @tags.reject! { |tag| tag == symbol }
      end
    end

    def tagged_with?(symbol)
      @tag_mutex.synchronize { @tags.include? symbol }
    end

    def register_event_handler(*guards, &block)
      register_handler :event, *guards, &block
    end

    def deliver_message(message)
      trigger_handler :event, message
    end
    alias << deliver_message

    def register_initial_handlers
      register_event_handler Punchblock::Event::Offer do |offer|
        @offer  = offer
        @client = offer.client
        throw :pass
      end

      register_event_handler Punchblock::HasHeaders do |event|
        variables.merge! event.headers_hash
        throw :pass
      end

      on_end do |event|
        hangup
        @end_reason_mutex.synchronize { @end_reason = event.reason }
        commands.terminate
      end
    end

    def on_end(&block)
      register_event_handler Punchblock::Event::End do |event|
        block.call event
        throw :pass
      end
    end

    def active?
      @end_reason_mutex.synchronize { !end_reason }
    end

    def accept(headers = nil)
      write_and_await_response Punchblock::Command::Accept.new(:headers => headers)
    end

    def answer(headers = nil)
      write_and_await_response Punchblock::Command::Answer.new(:headers => headers)
    end

    def reject(reason = :busy, headers = nil)
      write_and_await_response Punchblock::Command::Reject.new(:reason => reason, :headers => headers)
    end

    def hangup!(headers = nil)
      return false unless active?
      @end_reason_mutex.synchronize { @end_reason = true }
      write_and_await_response Punchblock::Command::Hangup.new(:headers => headers)
    end

    def hangup
      Adhearsion.active_calls.remove_inactive_call self
    end

    def join(other_call_id)
      write_and_await_response Punchblock::Command::Join.new :other_call_id => other_call_id
    end

    # Lock the socket for a command.  Can be used to allow the console to take
    # control of the thread in between AGI commands coming from the dialplan.
    def with_command_lock
      @command_monitor ||= Monitor.new
      @command_monitor.synchronize { yield }
    end

    def write_and_await_response(command, timeout = 60)
      logger.trace "Executing command #{command.inspect}"
      commands << command
      write_command command
      response = command.response timeout<commit_msg>[BUGFIX] Try out something that's causing CI to fail<commit_after>require 'thread'

module Adhearsion
  ##
  # Encapsulates call-related data and behavior.
  #
  class Call

    include HasGuardedHandlers

    attr_accessor :offer, :client, :end_reason, :commands, :variables

    delegate :[], :[]=, :to => :variables

    def initialize(offer = nil)
      register_initial_handlers

      @tag_mutex        = Mutex.new
      @tags             = []
      @end_reason_mutex = Mutex.new
      @commands         = CommandRegistry.new
      @variables        = SynchronizedHash.new

      self << offer if offer
    end

    def id
      offer.call_id
    end

    def tags
      @tag_mutex.synchronize { @tags.clone }
    end

    # This may still be a symbol, but no longer requires the tag to be a symbol although beware
    # that using a symbol would create a memory leak if used improperly
    # @param [String, Symbol] label String or Symbol with which to tag this call
    def tag(label)
      raise ArgumentError, "Tag must be a String or Symbol" unless [String, Symbol].include?(label.class)
      @tag_mutex.synchronize { @tags << label }
    end

    def remove_tag(symbol)
      @tag_mutex.synchronize do
        @tags.reject! { |tag| tag == symbol }
      end
    end

    def tagged_with?(symbol)
      @tag_mutex.synchronize { @tags.include? symbol }
    end

    def register_event_handler(*guards, &block)
      register_handler :event, *guards, &block
    end

    def deliver_message(message)
      trigger_handler :event, message
    end
    alias << deliver_message

    def register_initial_handlers
      register_event_handler Punchblock::Event::Offer do |offer|
        @offer  = offer
        @client = offer.client
        throw :pass
      end

      register_event_handler Punchblock::HasHeaders do |event|
        variables.merge! event.headers_hash
        throw :pass
      end

      on_end do |event|
        hangup
        @end_reason_mutex.synchronize { @end_reason = event.reason }
        commands.terminate
      end
    end

    def on_end(&block)
      register_event_handler Punchblock::Event::End do |event|
        block.call event
        throw :pass
      end
    end

    def active?
      @end_reason_mutex.synchronize { !end_reason }
    end

    def accept(headers = nil)
      write_and_await_response Punchblock::Command::Accept.new(:headers => headers)
    end

    def answer(headers = nil)
      write_and_await_response Punchblock::Command::Answer.new(:headers => headers)
    end

    def reject(reason = :busy, headers = nil)
      write_and_await_response Punchblock::Command::Reject.new(:reason => reason, :headers => headers)
    end

    def hangup!(headers = nil)
      return false unless active?
      @end_reason_mutex.synchronize { @end_reason = true }
      write_and_await_response Punchblock::Command::Hangup.new(:headers => headers)
    end

    def hangup
      Adhearsion.active_calls.remove_inactive_call self
    end

    def join(other_call_id)
      write_and_await_response Punchblock::Command::Join.new :other_call_id => other_call_id
    end

    # Lock the socket for a command.  Can be used to allow the console to take
    # control of the thread in between AGI commands coming from the dialplan.
    def with_command_lock
      @command_monitor ||= Monitor.new
      @command_monitor.synchronize { yield }
    end

    def write_and_await_response(command, timeout = 60)
      # TODO: Put this back once we figure out why it's causing CI to fail
      # logger.trace "Executing command #{command.inspect}"
      commands << command
      write_command command
      response = command.response timeout
{'fraction_non_alphanumeric': 0.07875862068965517, 'fraction_numerical': 0.0005517241379310345, 'mean_word_length': 2.7051609606540623, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 17, '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': '5734403', 'n_tokens_mistral': 2161, 'n_tokens_neox': 2098, 'n_words': 603}