text: module Listen
    module Adapters
  
      # Listener implementation for BSD's `kqueue`.
      #
      class BSD < Adapter
        extend DependencyManager
  
        # Declare the adapter's dependencies
        dependency 'rb-kqueue', '~> 0.2'
  
        # Watched kqueue events
        #
        # @see http://www.freebsd.org/cgi/man.cgi?query=kqueue
        # @see https://github.com/nex3/rb-kqueue/blob/master/lib/rb-kqueue/queue.rb
        #
        EVENTS = [ :delete, :write, :extend, :attrib, :link, :rename, :revoke ]
  
        # Initializes the Adapter. See {Listen::Adapter#initialize} for
        # more info.
        #
        def initialize(directories, options = {}, &callback)
          super
          @kqueue = init_kqueue
        end
  
        # Starts the adapter.
        #
        # @param [Boolean] blocking whether or not to block the current thread after starting
        #
        def start(blocking = true)
          @mutex.synchronize do
            return if @stop == false
            super
          end
  
          @kqueue_thread = Thread.new do
            until @stop
              @kqueue.poll
              sleep(@latency)
            end
          end
          @poll_thread   = Thread.new { poll_changed_dirs } if @report_changes
  
          @kqueue_thread.join if blocking
        end
  
        # Stops the adapter.
        #
        def stop
          @mutex.synchronize do
            return if @stop == true
            super
          end
  
          @kqueue.stop
          Thread.kill(@kqueue_thread) if @kqueue_thread
          @poll_thread.join if @poll_thread
        end
  
        # Checks if the adapter is usable on the current OS.
        #
        # @return [Boolean] whether usable or not
        #
        def self.usable?
          return false unless RbConfig::CONFIG['target_os'] =~ /freebsd/i
          super
        end
  
        private
  
        # Initializes a kqueue Queue and adds a watcher for each files in
        # the directories passed to the adapter.
        #
        # @return [INotify::Notifier] initialized kqueue
        #
        def init_kqueue
          require 'find'
  
          callback = lambda do |event|
            path = event.watcher.path
            @mutex.synchronize do
              # kqueue watches everything, but Listen only needs the
              # directory where stuffs happens.
              @changed_dirs << (File.directory?(path) ? path : File.dirname(path))
  
              # If it is a directory, and it has a write flag, it means a
              # file has been added so find out which and deal with it.
              # No need to check for removed file, kqueue will forget them
              # when the vfs does..
              if File.directory?(path) && !(event.flags & [:write]).empty?
                queue = event.watcher.queue
                Find.find(path) do |file|
                  unless queue.watchers.detect {|k,v| v.path == file.to_s}
                    queue.watch_file(file, *EVENTS, &callback)
                  end
                end
              end
            end
          end
  
          KQueue::Queue.new.tap do |queue|
            @directories.each do |directory|
              Find.find(directory) do |path|
                queue.watch_file(path, *EVENTS, &callback)
              end
            end
          end
        end
      end
    end
  end
  