File: em_scanner.rb

package info (click to toggle)
ruby-directory-watcher 1.5.1-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 224 kB
  • ctags: 224
  • sloc: ruby: 1,411; makefile: 5
file content (149 lines) | stat: -rw-r--r-- 4,341 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
begin
  require 'eventmachine'
  DirectoryWatcher::HAVE_EM = true
rescue LoadError
  DirectoryWatcher::HAVE_EM = false
end

if DirectoryWatcher::HAVE_EM

# Set up the appropriate polling options
[:epoll, :kqueue].each do |poll|
  if EventMachine.send("#{poll}?") then
    EventMachine.send("#{poll}=", true )
    break
  end
end

# The EmScanner uses the EventMachine reactor loop to monitor changes to
# files in the watched directory. This scanner is more efficient than the
# pure Ruby scanner because it relies on the operating system kernel
# notifications instead of a periodic polling and stat of every file in the
# watched directory (the technique used by the Scanner class).
#
# EventMachine cannot notify us when a file is added to the watched
# directory; therefore, added files are only picked up when we apply the
# glob pattern to the directory. This is done at the configured interval.
#
# Notes:
#
#  * Kqueue does not generate notifications when "touch" is used to update
#    a file's timestamp. This applies to Mac and BSD systems.
#
#  * New files are detected only when the watched directory is polled at the
#    configured interval.
#
class DirectoryWatcher::EmScanner < DirectoryWatcher::EventableScanner
  # call-seq:
  #    EmScanner.new( configuration )
  #
  def initialize( config )
    super(config)
  end

  # Called by EventablScanner#start to start the loop up and attach the periodic
  # timer that will poll the globs for new files.
  #
  def start_loop_with_attached_scan_timer
    return if @loop_thread
    unless EventMachine.reactor_running?
      @loop_thread = Thread.new {EventMachine.run}
      Thread.pass until EventMachine.reactor_running?
    end

    @timer = ScanTimer.new(self)
  end

  # Called by EventableScanner#stop to stop the loop as part of the shutdown
  # process.
  #
  def stop_loop
    if @loop_thread then
      EventMachine.next_tick do
        EventMachine.stop_event_loop
      end
      @loop_thread.kill
      @loop_thread = nil
    end
  end

  # :stopdoc:
  #
  # This is our tailored implementation of the EventMachine FileWatch class.
  # It receives notifications of file events and provides a mechanism to
  # translate the EventMachine events into objects to send to the Scanner that
  # it is initialized with.
  #
  # The Watcher only responds to modified and deleted events.
  #
  # This class is required by EventableScanner to institute file watching.
  #
  class Watcher < EventMachine::FileWatch
    def self.watch( path, scanner )
      EventMachine.watch_file path, Watcher, scanner
    end

    # Initialize the Watcher using with the given scanner
    # Post initialization, EventMachine will set @path
    #
    def initialize( scanner )
      @scanner = scanner
    end

    # EventMachine callback for when a watched file is deleted. We convert this
    # to a FileStat object for a removed file.
    #
    def file_deleted
      @scanner.on_removed(self, ::DirectoryWatcher::FileStat.for_removed_path(@path))
    end
    # Event Machine also sends events on file_moved which we'll just consider a
    # file deleted and the file added event will be picked up by the next scan
    alias :file_moved :file_deleted

    # EventMachine callback for when a watched file is modified. We convert this
    # to a FileStat object and send it to the collector
    def file_modified
      stat = File.stat @path
      @scanner.on_modified(self, ::DirectoryWatcher::FileStat.new(@path, stat.mtime, stat.size))
    end

    # Detach the watcher from the event loop.
    #
    # Required by EventableScanner as part of the shutdown process.
    #
    def detach
      EventMachine.next_tick do
        stop_watching
      end
    end
  end

  # Periodically execute a Scan.
  #
  # This object is used by EventableScanner to during shutdown.
  #
  class ScanTimer
    def initialize( scanner )
      @scanner = scanner
      @timer = EventMachine::PeriodicTimer.new( @scanner.interval, method(:on_scan) )
    end

    def on_scan
      @scanner.on_scan
    end

    # Detach the watcher from the event loop.
    #
    # Required by EventableScanner as part of the shutdown process.
    #
    def detach
      EventMachine.next_tick do
        @timer.cancel
      end
    end
  end
  # :startdoc:
end  # class DirectoryWatcher::EmScanner
end  # if HAVE_EM

# EOF