logstash-input-file 4.1.3 → 4.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/JAR_VERSION +1 -1
  4. data/README.md +0 -3
  5. data/docs/index.asciidoc +26 -16
  6. data/lib/filewatch/bootstrap.rb +10 -21
  7. data/lib/filewatch/discoverer.rb +35 -28
  8. data/lib/filewatch/observing_base.rb +2 -1
  9. data/lib/filewatch/read_mode/handlers/base.rb +19 -6
  10. data/lib/filewatch/read_mode/handlers/read_file.rb +43 -32
  11. data/lib/filewatch/read_mode/handlers/read_zip_file.rb +8 -3
  12. data/lib/filewatch/read_mode/processor.rb +8 -8
  13. data/lib/filewatch/settings.rb +3 -3
  14. data/lib/filewatch/sincedb_collection.rb +56 -42
  15. data/lib/filewatch/sincedb_value.rb +6 -0
  16. data/lib/filewatch/stat/generic.rb +34 -0
  17. data/lib/filewatch/stat/windows_path.rb +32 -0
  18. data/lib/filewatch/tail_mode/handlers/base.rb +40 -22
  19. data/lib/filewatch/tail_mode/handlers/create.rb +1 -2
  20. data/lib/filewatch/tail_mode/handlers/create_initial.rb +2 -1
  21. data/lib/filewatch/tail_mode/handlers/delete.rb +13 -1
  22. data/lib/filewatch/tail_mode/handlers/grow.rb +5 -2
  23. data/lib/filewatch/tail_mode/handlers/shrink.rb +7 -4
  24. data/lib/filewatch/tail_mode/handlers/unignore.rb +4 -2
  25. data/lib/filewatch/tail_mode/processor.rb +147 -58
  26. data/lib/filewatch/watch.rb +15 -35
  27. data/lib/filewatch/watched_file.rb +237 -41
  28. data/lib/filewatch/watched_files_collection.rb +2 -2
  29. data/lib/filewatch/winhelper.rb +167 -25
  30. data/lib/jars/filewatch-1.0.1.jar +0 -0
  31. data/lib/logstash/inputs/file.rb +9 -2
  32. data/logstash-input-file.gemspec +9 -2
  33. data/spec/file_ext/file_ext_windows_spec.rb +36 -0
  34. data/spec/filewatch/read_mode_handlers_read_file_spec.rb +2 -2
  35. data/spec/filewatch/reading_spec.rb +100 -57
  36. data/spec/filewatch/rotate_spec.rb +451 -0
  37. data/spec/filewatch/spec_helper.rb +33 -10
  38. data/spec/filewatch/tailing_spec.rb +273 -153
  39. data/spec/filewatch/watched_file_spec.rb +3 -3
  40. data/spec/filewatch/watched_files_collection_spec.rb +3 -3
  41. data/spec/filewatch/winhelper_spec.rb +4 -5
  42. data/spec/helpers/logging_level_helper.rb +8 -0
  43. data/spec/helpers/rspec_wait_handler_helper.rb +38 -0
  44. data/spec/helpers/spec_helper.rb +7 -1
  45. data/spec/inputs/file_read_spec.rb +54 -24
  46. data/spec/inputs/file_tail_spec.rb +244 -284
  47. metadata +13 -3
  48. data/lib/jars/filewatch-1.0.0.jar +0 -0
@@ -10,8 +10,7 @@ module FileWatch module TailMode module Handlers
10
10
 
11
11
  def update_existing_specifically(watched_file, sincedb_value)
12
12
  # sincedb_value is the source of truth
13
- position = sincedb_value.position
14
- watched_file.update_bytes_read(position)
13
+ watched_file.update_bytes_read(sincedb_value.position)
15
14
  end
16
15
  end
17
16
  end end end
@@ -4,6 +4,7 @@ module FileWatch module TailMode module Handlers
4
4
  class CreateInitial < Base
5
5
  def handle_specifically(watched_file)
6
6
  if open_file(watched_file)
7
+ logger.trace("handle_specifically opened file handle: #{watched_file.file.fileno}, path: #{watched_file.filename}")
7
8
  add_or_update_sincedb_collection(watched_file)
8
9
  end
9
10
  end
@@ -13,7 +14,7 @@ module FileWatch module TailMode module Handlers
13
14
  if @settings.start_new_files_at == :beginning
14
15
  position = 0
15
16
  end
16
- logger.debug("update_existing_specifically - #{watched_file.path}: seeking to #{position}")
17
+ logger.trace("update_existing_specifically - #{watched_file.path}: seeking to #{position}")
17
18
  watched_file.update_bytes_read(position)
18
19
  sincedb_value.update_position(position)
19
20
  end
@@ -2,9 +2,21 @@
2
2
 
3
3
  module FileWatch module TailMode module Handlers
4
4
  class Delete < Base
5
+ DATA_LOSS_WARNING = "watched file path was deleted or rotated before all content was read, if the file is found again it will be read from the last position"
5
6
  def handle_specifically(watched_file)
7
+ # TODO consider trying to find the renamed file - it will have the same inode.
8
+ # Needs a rotate scheme rename hint from user e.g. "<name>-YYYY-MM-DD-N.<ext>" or "<name>.<ext>.N"
9
+ # send the found content to the same listener (stream identity)
10
+ logger.trace("info",
11
+ "watched_file details" => watched_file.details,
12
+ "path" => watched_file.path)
13
+ if watched_file.bytes_unread > 0
14
+ logger.warn(DATA_LOSS_WARNING, "unread_bytes" => watched_file.bytes_unread, "path" => watched_file.path)
15
+ end
6
16
  watched_file.listener.deleted
7
- sincedb_collection.unset_watched_file(watched_file)
17
+ # no need to worry about data in the buffer
18
+ # if found it will be associated by inode and read from last position
19
+ sincedb_collection.watched_file_deleted(watched_file)
8
20
  watched_file.file_close
9
21
  end
10
22
  end
@@ -4,8 +4,11 @@ module FileWatch module TailMode module Handlers
4
4
  class Grow < Base
5
5
  def handle_specifically(watched_file)
6
6
  watched_file.file_seek(watched_file.bytes_read)
7
- logger.debug("reading to eof: #{watched_file.path}")
8
- read_to_eof(watched_file)
7
+ loop do
8
+ loop_control = watched_file.loop_control_adjusted_for_stat_size
9
+ controlled_read(watched_file, loop_control)
10
+ break unless loop_control.more
11
+ end
9
12
  end
10
13
  end
11
14
  end end end
@@ -5,16 +5,19 @@ module FileWatch module TailMode module Handlers
5
5
  def handle_specifically(watched_file)
6
6
  add_or_update_sincedb_collection(watched_file)
7
7
  watched_file.file_seek(watched_file.bytes_read)
8
- logger.debug("reading to eof: #{watched_file.path}")
9
- read_to_eof(watched_file)
8
+ loop do
9
+ loop_control = watched_file.loop_control_adjusted_for_stat_size
10
+ controlled_read(watched_file, loop_control)
11
+ break unless loop_control.more
12
+ end
10
13
  end
11
14
 
12
15
  def update_existing_specifically(watched_file, sincedb_value)
13
16
  # we have a match but size is smaller
14
17
  # set all to zero
15
- logger.debug("update_existing_specifically: #{watched_file.path}: was truncated seeking to beginning")
16
- watched_file.update_bytes_read(0) if watched_file.bytes_read != 0
18
+ watched_file.reset_bytes_unread
17
19
  sincedb_value.update_position(0)
20
+ logger.trace("update_existing_specifically: was truncated seeking to beginning", "watched file" => watched_file.details, "sincedb value" => sincedb_value)
18
21
  end
19
22
  end
20
23
  end end end
@@ -6,15 +6,16 @@ module FileWatch module TailMode module Handlers
6
6
  # before any other handling has been done
7
7
  # at a minimum we create or associate a sincedb value
8
8
  def handle_specifically(watched_file)
9
- add_or_update_sincedb_collection(watched_file) unless sincedb_collection.member?(watched_file.sincedb_key)
9
+ add_or_update_sincedb_collection(watched_file)
10
10
  end
11
11
 
12
12
  def get_new_value_specifically(watched_file)
13
13
  # for file initially ignored their bytes_read was set to stat.size
14
14
  # use this value not the `start_new_files_at` for the position
15
- # logger.debug("get_new_value_specifically", "watched_file" => watched_file.inspect)
15
+ # logger.trace("get_new_value_specifically", "watched_file" => watched_file.inspect)
16
16
  SincedbValue.new(watched_file.bytes_read).tap do |val|
17
17
  val.set_watched_file(watched_file)
18
+ logger.trace("-------------------- >>>>> get_new_value_specifically: unignore", "watched file" => watched_file.details, "sincedb value" => val)
18
19
  end
19
20
  end
20
21
 
@@ -25,6 +26,7 @@ module FileWatch module TailMode module Handlers
25
26
  # we will handle grow or shrink
26
27
  # for now we seek to where we were before the file got ignored (grow)
27
28
  # or to the start (shrink)
29
+ logger.trace("-------------------- >>>>> update_existing_specifically: unignore", "watched file" => watched_file.details, "sincedb value" => sincedb_value)
28
30
  position = 0
29
31
  if watched_file.shrunk?
30
32
  watched_file.update_bytes_read(0)
@@ -34,6 +34,7 @@ module FileWatch module TailMode
34
34
  end
35
35
 
36
36
  def initialize_handlers(sincedb_collection, observer)
37
+ @sincedb_collection = sincedb_collection
37
38
  @create_initial = Handlers::CreateInitial.new(sincedb_collection, observer, @settings)
38
39
  @create = Handlers::Create.new(sincedb_collection, observer, @settings)
39
40
  @grow = Handlers::Grow.new(sincedb_collection, observer, @settings)
@@ -71,80 +72,148 @@ module FileWatch module TailMode
71
72
  @unignore.handle(watched_file)
72
73
  end
73
74
 
75
+ def process_all_states(watched_files)
76
+ process_closed(watched_files)
77
+ return if watch.quit?
78
+ process_ignored(watched_files)
79
+ return if watch.quit?
80
+ process_delayed_delete(watched_files)
81
+ return if watch.quit?
82
+ process_restat_for_watched_and_active(watched_files)
83
+ return if watch.quit?
84
+ process_rotation_in_progress(watched_files)
85
+ return if watch.quit?
86
+ process_watched(watched_files)
87
+ return if watch.quit?
88
+ process_active(watched_files)
89
+ end
90
+
91
+ private
92
+
74
93
  def process_closed(watched_files)
75
- logger.debug("Closed processing")
94
+ # logger.trace("Closed processing")
76
95
  # Handles watched_files in the closed state.
77
96
  # if its size changed it is put into the watched state
78
97
  watched_files.select {|wf| wf.closed? }.each do |watched_file|
79
- path = watched_file.path
80
- begin
81
- watched_file.restat
98
+ common_restat_with_delay(watched_file, "Closed") do
99
+ # it won't do this if rotation is detected
82
100
  if watched_file.size_changed?
83
101
  # if the closed file changed, move it to the watched state
84
102
  # not to active state because we want to respect the active files window.
85
103
  watched_file.watch
86
104
  end
87
- rescue Errno::ENOENT
88
- # file has gone away or we can't read it anymore.
89
- common_deleted_reaction(watched_file, "Closed")
90
- rescue => e
91
- common_error_reaction(path, e, "Closed")
92
105
  end
93
106
  break if watch.quit?
94
107
  end
95
108
  end
96
109
 
97
110
  def process_ignored(watched_files)
98
- logger.debug("Ignored processing")
111
+ # logger.trace("Ignored processing")
99
112
  # Handles watched_files in the ignored state.
100
113
  # if its size changed:
101
114
  # put it in the watched state
102
115
  # invoke unignore
103
116
  watched_files.select {|wf| wf.ignored? }.each do |watched_file|
104
- path = watched_file.path
105
- begin
106
- watched_file.restat
117
+ common_restat_with_delay(watched_file, "Ignored") do
118
+ # it won't do this if rotation is detected
107
119
  if watched_file.size_changed?
108
120
  watched_file.watch
109
121
  unignore(watched_file)
110
122
  end
111
- rescue Errno::ENOENT
112
- # file has gone away or we can't read it anymore.
113
- common_deleted_reaction(watched_file, "Ignored")
114
- rescue => e
115
- common_error_reaction(path, e, "Ignored")
116
123
  end
117
124
  break if watch.quit?
118
125
  end
119
126
  end
120
127
 
128
+ def process_delayed_delete(watched_files)
129
+ # defer the delete to one loop later to ensure that the stat really really can't find a renamed file
130
+ # because a `stat` can be called right in the middle of the rotation rename cascade
131
+ logger.trace("Delayed Delete processing")
132
+ watched_files.select {|wf| wf.delayed_delete?}.each do |watched_file|
133
+ logger.trace(">>> Delayed Delete", "path" => watched_file.filename)
134
+ common_restat_without_delay(watched_file, ">>> Delayed Delete") do
135
+ logger.trace(">>> Delayed Delete: file at path found again", "watched_file" => watched_file.details)
136
+ watched_file.file_at_path_found_again
137
+ end
138
+ end
139
+ end
140
+
141
+ def process_restat_for_watched_and_active(watched_files)
142
+ # do restat on all watched and active states once now. closed and ignored have been handled already
143
+ logger.trace("Watched + Active restat processing")
144
+ watched_files.select {|wf| wf.watched? || wf.active?}.each do |watched_file|
145
+ common_restat_with_delay(watched_file, "Watched")
146
+ end
147
+ end
148
+
149
+ def process_rotation_in_progress(watched_files)
150
+ logger.trace("Rotation In Progress processing")
151
+ watched_files.select {|wf| wf.rotation_in_progress?}.each do |watched_file|
152
+ if !watched_file.all_read?
153
+ if watched_file.file_open?
154
+ # rotated file but original opened file is not fully read
155
+ # we need to keep reading the open file, if we close it we lose it because the path is now pointing at a different file.
156
+ logger.trace(">>> Rotation In Progress - inode change detected and original content is not fully read, reading all", "watched_file details" => watched_file.details)
157
+ # need to fully read open file while we can
158
+ watched_file.set_maximum_read_loop
159
+ grow(watched_file)
160
+ watched_file.set_standard_read_loop
161
+ else
162
+ logger.warn(">>> Rotation In Progress - inode change detected and original content is not fully read, file is closed and path points to new content", "watched_file details" => watched_file.details)
163
+ end
164
+ end
165
+ current_key = watched_file.sincedb_key
166
+ sdb_value = @sincedb_collection.get(current_key)
167
+ potential_key = watched_file.stat_sincedb_key
168
+ potential_sdb_value = @sincedb_collection.get(potential_key)
169
+ logger.trace(">>> Rotation In Progress", "watched_file" => watched_file.details, "found_sdb_value" => sdb_value, "potential_key" => potential_key, "potential_sdb_value" => potential_sdb_value)
170
+ if potential_sdb_value.nil?
171
+ if sdb_value.nil?
172
+ logger.trace("---------- >>> Rotation In Progress: rotating as initial file, no potential sincedb value AND no found sincedb value")
173
+ watched_file.rotate_as_initial_file
174
+ else
175
+ logger.trace("---------- >>>> Rotation In Progress: rotating as existing file, no potential sincedb value BUT found sincedb value")
176
+ watched_file.rotate_as_file
177
+ sdb_value.clear_watched_file
178
+ end
179
+ new_sdb_value = SincedbValue.new(0)
180
+ new_sdb_value.set_watched_file(watched_file)
181
+ @sincedb_collection.set(potential_key, new_sdb_value)
182
+ else
183
+ other_watched_file = potential_sdb_value.watched_file
184
+ if other_watched_file.nil?
185
+ logger.trace("---------- >>>> Rotation In Progress: rotating as existing file WITH potential sincedb value that does not have a watched file reference !!!!!!!!!!!!!!!!!")
186
+ watched_file.rotate_as_file(potential_sdb_value.position)
187
+ sdb_value.clear_watched_file unless sdb_value.nil?
188
+ potential_sdb_value.set_watched_file(watched_file)
189
+ else
190
+ logger.trace("---------- >>>> Rotation In Progress: rotating from...", "this watched_file details" => watched_file.details, "other watched_file details" => other_watched_file.details)
191
+ watched_file.rotate_from(other_watched_file)
192
+ sdb_value.clear_watched_file unless sdb_value.nil?
193
+ potential_sdb_value.set_watched_file(watched_file)
194
+ end
195
+ end
196
+ logger.trace("---------- >>>> Rotation In Progress: after handling rotation", "this watched_file details" => watched_file.details, "sincedb_value" => (potential_sdb_value || sdb_value))
197
+ end
198
+ end
199
+
121
200
  def process_watched(watched_files)
122
- logger.debug("Watched processing")
123
201
  # Handles watched_files in the watched state.
124
202
  # for a slice of them:
125
203
  # move to the active state
126
204
  # and we allow the block to open the file and create a sincedb collection record if needed
127
205
  # some have never been active and some have
128
206
  # those that were active before but are watched now were closed under constraint
129
-
207
+ logger.trace("Watched processing")
130
208
  # how much of the max active window is available
131
209
  to_take = @settings.max_active - watched_files.count{|wf| wf.active?}
132
210
  if to_take > 0
133
211
  watched_files.select {|wf| wf.watched?}.take(to_take).each do |watched_file|
134
- path = watched_file.path
135
- begin
136
- watched_file.restat
137
- watched_file.activate
138
- if watched_file.initial?
139
- create_initial(watched_file)
140
- else
141
- create(watched_file)
142
- end
143
- rescue Errno::ENOENT
144
- # file has gone away or we can't read it anymore.
145
- common_deleted_reaction(watched_file, "Watched")
146
- rescue => e
147
- common_error_reaction(path, e, "Watched")
212
+ watched_file.activate
213
+ if watched_file.initial?
214
+ create_initial(watched_file)
215
+ else
216
+ create(watched_file)
148
217
  end
149
218
  break if watch.quit?
150
219
  end
@@ -159,51 +228,71 @@ module FileWatch module TailMode
159
228
  end
160
229
 
161
230
  def process_active(watched_files)
162
- logger.debug("Active processing")
231
+ # logger.trace("Active processing")
163
232
  # Handles watched_files in the active state.
164
- # it has been read once - unless they were empty at the time
233
+ # files have been opened at this point
165
234
  watched_files.select {|wf| wf.active? }.each do |watched_file|
166
- path = watched_file.path
167
- begin
168
- watched_file.restat
169
- rescue Errno::ENOENT
170
- # file has gone away or we can't read it anymore.
171
- common_deleted_reaction(watched_file, "Active")
172
- next
173
- rescue => e
174
- common_error_reaction(path, e, "Active")
175
- next
176
- end
177
235
  break if watch.quit?
236
+ path = watched_file.filename
178
237
  if watched_file.grown?
179
- logger.debug("Active - file grew: #{path}: new size is #{watched_file.last_stat_size}, old size #{watched_file.bytes_read}")
238
+ logger.trace("Active - file grew: #{path}: new size is #{watched_file.last_stat_size}, bytes read #{watched_file.bytes_read}")
180
239
  grow(watched_file)
181
240
  elsif watched_file.shrunk?
241
+ if watched_file.bytes_unread > 0
242
+ logger.warn("Active - shrunk: DATA LOSS!! truncate detected with #{watched_file.bytes_unread} unread bytes: #{path}")
243
+ end
182
244
  # we don't update the size here, its updated when we actually read
183
- logger.debug("Active - file shrunk #{path}: new size is #{watched_file.last_stat_size}, old size #{watched_file.bytes_read}")
245
+ logger.trace("Active - file shrunk #{path}: new size is #{watched_file.last_stat_size}, old size #{watched_file.bytes_read}")
184
246
  shrink(watched_file)
185
247
  else
186
248
  # same size, do nothing
249
+ logger.trace("Active - no change", "watched_file" => watched_file.details)
187
250
  end
188
251
  # can any active files be closed to make way for waiting files?
189
252
  if watched_file.file_closable?
190
- logger.debug("Watch each: active: file expired: #{path}")
253
+ logger.trace("Watch each: active: file expired: #{path}")
191
254
  timeout(watched_file)
192
255
  watched_file.close
193
256
  end
194
257
  end
195
258
  end
196
259
 
197
- def common_deleted_reaction(watched_file, action)
198
- # file has gone away or we can't read it anymore.
199
- watched_file.unwatch
200
- delete(watched_file)
201
- deletable_filepaths << watched_file.path
202
- logger.debug("#{action} - stat failed: #{watched_file.path}, removing from collection")
260
+ def common_restat_with_delay(watched_file, action, &block)
261
+ common_restat(watched_file, action, true, &block)
203
262
  end
204
263
 
205
- def common_error_reaction(path, error, action)
206
- logger.error("#{action} - other error #{path}: (#{error.message}, #{error.backtrace.take(8).inspect})")
264
+ def common_restat_without_delay(watched_file, action, &block)
265
+ common_restat(watched_file, action, false, &block)
266
+ end
267
+
268
+ def common_restat(watched_file, action, delay, &block)
269
+ all_ok = true
270
+ begin
271
+ watched_file.restat
272
+ if watched_file.rotation_in_progress?
273
+ logger.trace("-------------------- >>>>> restat - rotation_detected", "watched_file details" => watched_file.details, "new sincedb key" => watched_file.stat_sincedb_key)
274
+ # don't yield to closed and ignore processing
275
+ else
276
+ yield if block_given?
277
+ end
278
+ rescue Errno::ENOENT
279
+ if delay
280
+ logger.trace("#{action} - delaying the stat fail on: #{watched_file.filename}")
281
+ watched_file.delay_delete
282
+ else
283
+ # file has gone away or we can't read it anymore.
284
+ logger.trace("#{action} - after a delay, really can't find this file: #{watched_file.filename}")
285
+ watched_file.unwatch
286
+ logger.trace("#{action} - removing from collection: #{watched_file.filename}")
287
+ delete(watched_file)
288
+ deletable_filepaths << watched_file.path
289
+ all_ok = false
290
+ end
291
+ rescue => e
292
+ logger.error("#{action} - other error #{watched_file.path}: (#{e.message}, #{e.backtrace.take(8).inspect})")
293
+ all_ok = false
294
+ end
295
+ all_ok
207
296
  end
208
297
  end
209
298
  end end
@@ -10,11 +10,8 @@ module FileWatch
10
10
 
11
11
  def initialize(discoverer, watched_files_collection, settings)
12
12
  @settings = settings
13
- # watch and iterate_on_state can be called from different threads.
14
- @lock = Mutex.new
15
13
  # we need to be threadsafe about the quit mutation
16
- @quit = false
17
- @quit_lock = Mutex.new
14
+ @quit = Concurrent::AtomicBoolean.new(false)
18
15
  @lastwarn_max_files = 0
19
16
  @discoverer = discoverer
20
17
  @watched_files_collection = watched_files_collection
@@ -27,17 +24,13 @@ module FileWatch
27
24
  end
28
25
 
29
26
  def watch(path)
30
- synchronized do
31
- @discoverer.add_path(path)
32
- end
27
+ @discoverer.add_path(path)
33
28
  # don't return whatever @discoverer.add_path returns
34
29
  return true
35
30
  end
36
31
 
37
32
  def discover
38
- synchronized do
39
- @discoverer.discover
40
- end
33
+ @discoverer.discover
41
34
  # don't return whatever @discoverer.discover returns
42
35
  return true
43
36
  end
@@ -60,6 +53,7 @@ module FileWatch
60
53
  break if quit?
61
54
  sleep(@settings.stat_interval)
62
55
  end
56
+ sincedb_collection.write_if_requested # does nothing if no requests to write were lodged.
63
57
  @watched_files_collection.close_all
64
58
  end # def subscribe
65
59
 
@@ -67,42 +61,28 @@ module FileWatch
67
61
  # differently from Tail mode - see the ReadMode::Processor and TailMode::Processor
68
62
  def iterate_on_state
69
63
  return if @watched_files_collection.empty?
70
- synchronized do
71
- begin
72
- # creates this snapshot of watched_file values just once
73
- watched_files = @watched_files_collection.values
74
- @processor.process_closed(watched_files)
75
- return if quit?
76
- @processor.process_ignored(watched_files)
77
- return if quit?
78
- @processor.process_watched(watched_files)
79
- return if quit?
80
- @processor.process_active(watched_files)
81
- ensure
82
- @watched_files_collection.delete(@processor.deletable_filepaths)
83
- @processor.deletable_filepaths.clear
84
- end
64
+ begin
65
+ # creates this snapshot of watched_file values just once
66
+ watched_files = @watched_files_collection.values
67
+ @processor.process_all_states(watched_files)
68
+ ensure
69
+ @watched_files_collection.delete(@processor.deletable_filepaths)
70
+ @processor.deletable_filepaths.clear
85
71
  end
86
72
  end # def each
87
73
 
88
74
  def quit
89
- @quit_lock.synchronize do
90
- @quit = true
91
- end
92
- end # def quit
75
+ @quit.make_true
76
+ end
93
77
 
94
78
  def quit?
95
- @quit_lock.synchronize { @quit }
79
+ @quit.true?
96
80
  end
97
81
 
98
82
  private
99
83
 
100
- def synchronized(&block)
101
- @lock.synchronize { block.call }
102
- end
103
-
104
84
  def reset_quit
105
- @quit_lock.synchronize { @quit = false }
85
+ @quit.make_false
106
86
  end
107
87
  end
108
88
  end