<commit_before>require 'em-websocket'
require 'multi_json'

module Guard
  class LiveReload
    class Reactor

      attr_reader :thread, :web_sockets

      def initialize(options)
        @web_sockets = []
        @options     = options
        @thread      = start_threaded_reactor(options)
      end

      def stop
        thread.kill
      end

      def reload_browser(paths = [])
        UI.info "Reloading browser: #{paths.join(' ')}"
        paths.each do |path|
          data = MultiJson.encode(['refresh', {
            :path           => "#{Dir.pwd}/#{path}",
            :apply_js_live  => @options[:apply_js_live],
            :apply_css_live => @options[:apply_css_live]
          }])
          UI.debug data
          @web_sockets.each { |ws| ws.send(data) }
        end
      end

    private

      def start_threaded_reactor(options)
        Thread.new do
          EventMachine.run do
            UI.info "LiveReload #{options[:api_version]} is waiting for a browser to connect."
            EventMachine.start_server(options[:host], options[:port], EventMachine::WebSocket::Connection, {}) do |ws|
              ws.onopen do
                begin
                  UI.info "Browser connected."
                  ws.send "!!ver:#{options[:api_version]}"
                  @web_sockets << ws
                rescue
                  UI.errror $!
                  UI.errror $!.backtrace
                end
              end

              ws.onmessage do |msg|
                UI.info "Browser URL: #{msg}"
              end

              ws.onclose do
                @web_sockets.delete ws
                UI.info "Browser disconnected."
              end
            end
          end
        end
      end

    end
  end
end<commit_msg>Protect @web_sockets to prevent deadlocks<commit_after>require 'em-websocket'
require 'multi_json'
require 'thread'

module Guard
  class LiveReload
    class Reactor

      attr_reader :thread, :web_sockets

      def initialize(options)
        @mutex = Mutex.new
        @web_sockets = []
        @options     = options
        @thread      = start_threaded_reactor(options)
      end

      def stop
        thread.kill
      end

      def reload_browser(paths = [])
        @mutex.synchronize do
          UI.info "Reloading browser: #{paths.join(' ')}"
          paths.each do |path|
            data = MultiJson.encode(['refresh', {
              :path           => "#{Dir.pwd}/#{path}",
              :apply_js_live  => @options[:apply_js_live],
              :apply_css_live => @options[:apply_css_live]
            }])
            UI.debug data
            @web_sockets.each { |ws| ws.send(data) }
          end
        end
      end

    private

      def start_threaded_reactor(options)
        Thread.new do
          EventMachine.run do
            UI.info "LiveReload #{options[:api_version]} is waiting for a browser to connect."
            EventMachine.start_server(options[:host], options[:port], EventMachine::WebSocket::Connection, {}) do |ws|
              ws.onopen do
                @mutex.synchronize do
                  begin
                    UI.info "Browser connected."
                    ws.send "!!ver:#{options[:api_version]}"
                    @web_sockets << ws
                  rescue
                    UI.errror $!
                    UI.errror $!.backtrace
                  end
                end
              end

              ws.onmessage do |msg|
                UI.info "Browser URL: #{msg}"
              end

              ws.onclose do
                @mutex.synchronize do
                  @web_sockets.delete ws
                  UI.info "Browser disconnected."
                end
              end
            end
          end
        end
      end

    end
  end
end
{'fraction_non_alphanumeric': 0.09378292939936776, 'fraction_numerical': 0.0, 'mean_word_length': 1.356921166977033, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, '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': '904037', 'n_tokens_mistral': 1074, 'n_tokens_neox': 1012, 'n_words': 236}