Permalink
Browse files

Fix a race condition when stopping Guard

Guard used to delegate blocking the execution to the listener. When the listener
was stopped, execution was resumed on the main thread of Guard. Since there is nothing
more blocking the main thread, the process ended. Any other thread
running was also killed when the process ended.

The Listen turnstile is used now to block execution until Guard is allowed to stop. This allows
any code after stopping the listener to be executed even if it's run from a thread other than
the Guard main thread (like the CLI thread).
  • Loading branch information...
1 parent 4508ecb commit 7099ed9ec18f770f336afec9afb1fcd5d10191d5 @Maher4Ever Maher4Ever committed Jun 20, 2012
Showing with 12 additions and 7 deletions.
  1. +9 −5 lib/guard.rb
  2. +3 −2 spec/guard_spec.rb
View
@@ -45,6 +45,7 @@ def setup(options = {})
@options = options
@watchdir = (options[:watchdir] && File.expand_path(options[:watchdir])) || Dir.pwd
@runner = Runner.new
+ @allow_stop = Listen::Turnstile.new
UI.clear
deprecated_options_warning
@@ -104,7 +105,7 @@ def setup_signal_traps
def setup_listener
listener_callback = lambda do |modified, added, removed|
Dsl.reevaluate_guardfile if Watcher.match_guardfile?(modified)
-
+
::Guard.within_preserved_state do
runner.run_on_changes(modified, added, removed)
end
@@ -156,18 +157,21 @@ def start(options = {})
within_preserved_state do
runner.run(:start)
end
-
- listener.start
+
+ listener.start(false)
+
+ @allow_stop.wait if @allow_stop
end
# Stop Guard listening to file changes
#
def stop
+ listener.stop
interactor.stop if interactor
runner.run(:stop)
UI.info 'Bye bye...', :reset => true
- #TODO: Move to top when https://github.com/guard/listen/issues/46 is resolved
- listener.stop
+
+ @allow_stop.signal if @allow_stop
end
# Reload Guardfile and all Guards currently enabled.
View
@@ -2,7 +2,7 @@
describe Guard do
before { ::Guard::Interactor.stub(:fabricate) }
-
+
describe ".setup" do
let(:options) { { :my_opts => true, :guardfile => File.join(@fixture_path, "Guardfile") } }
subject { ::Guard.setup(options) }
@@ -465,6 +465,7 @@ class Guard::FooBaz < Guard::Guard; end
::Guard.stub(:listener => mock('listener', :start => true))
::Guard.stub(:runner => mock('runner', :run => true))
::Guard.stub(:within_preserved_state).and_yield
+ ::Guard.instance_variable_set('@allow_stop', mock('turnstile', :wait => true, :signal => true))
end
it "setup Guard" do
@@ -586,7 +587,7 @@ class Guard::FooBaz < Guard::Guard; end
describe '.within_preserved_state' do
subject { ::Guard.setup }
before { subject.interactor = stub('interactor').as_null_object }
-
+
it 'disables the interactor before running the block and then re-enables it when done' do
subject.interactor.should_receive(:stop)
subject.interactor.should_receive(:start)

0 comments on commit 7099ed9

Please sign in to comment.