Skip to content

Commit

Permalink
Fix a synchronisation bug
Browse files Browse the repository at this point in the history
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
Maher4Ever committed Apr 3, 2012
1 parent a331537 commit fb08ed2
Showing 1 changed file with 13 additions and 11 deletions.
24 changes: 13 additions & 11 deletions lib/rb-fsevent/fsevent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down

0 comments on commit fb08ed2

Please sign in to comment.