Permalink
Browse files

Fix a synchronisation bug

The run method will almost always be called inside a thread. This commit fixes
a synchronisation problem between the run and stop methods by explicitly opening
a pipe on the beginning of the run method.

The source of the problem is the fact that when someone stops the watcher,
the @path would become nil and calling the private method pipe would create a new pipe.

A sitiuation where this could be a problem is when the run method is waiting on changes
(blocking on select), note here that @running does not matter anymore, the code is blocking
on select!

Stoping the watcher and firing an event at the same time would unblock the run method
and getting the results in the old code would have created a new pipe and blocked on readline.
  • Loading branch information...
1 parent a331537 commit fb08ed268339fcf93551a64031008519e9ec6505 @Maher4Ever Maher4Ever committed Apr 3, 2012
Showing with 13 additions and 11 deletions.
  1. +13 −11 lib/rb-fsevent/fsevent.rb
View
@@ -32,12 +32,14 @@ def watch(watch_paths, options=nil, &block)
end
def run
+ @pipe = open_pipe
@running = true
+
# please note the use of IO::select() here, as it is used specifically to
# preserve correct signal handling behavior in ruby 1.8.
- while @running && IO::select([pipe], nil, nil, nil)
- if line = pipe.readline
- modified_dir_paths = line.split(":").select { |dir| dir != "\n" }
+ while @running && IO::select([@pipe], nil, nil, nil)
+ if line = @pipe.readline
+ modified_dir_paths = line.split(':').select { |dir| dir != "\n" }
callback.call(modified_dir_paths)
end
end
@@ -47,18 +49,18 @@ def run
end
def stop
- if pipe
- Process.kill("KILL", pipe.pid)
- pipe.close
+ unless @pipe.nil?
+ Process.kill('KILL', @pipe.pid)
+ @pipe.close
end
rescue IOError
ensure
- @pipe = @running = nil
+ @running = false
end
if RUBY_VERSION < '1.9'
- def pipe
- @pipe ||= IO.popen("#{self.class.watcher_path} #{options_string} #{shellescaped_paths}")
+ def open_pipe
+ IO.popen("#{self.class.watcher_path} #{options_string} #{shellescaped_paths}")
end
private
@@ -89,8 +91,8 @@ def shellescape(str)
return str
end
else
- def pipe
- @pipe ||= IO.popen([self.class.watcher_path] + @options + @paths)
+ def open_pipe
+ IO.popen([self.class.watcher_path] + @options + @paths)
end
end

0 comments on commit fb08ed2

Please sign in to comment.