diff --git a/lib/spring/watcher/polling.rb b/lib/spring/watcher/polling.rb index 9fb5ec4c..3d3312a7 100644 --- a/lib/spring/watcher/polling.rb +++ b/lib/spring/watcher/polling.rb @@ -33,7 +33,7 @@ def start Thread.current.abort_on_exception = true begin - loop do + until stale? Kernel.sleep latency check_stale end @@ -42,6 +42,8 @@ def start "poller: aborted: #{e.class}: #{e}\n #{e.backtrace.join("\n ")}" end raise + ensure + @poller = nil end } end @@ -55,6 +57,10 @@ def stop end end + def running? + @poller && @poller.alive? + end + def subjects_changed computed = compute_mtime debug { "subjects_changed: mtime #{@mtime} -> #{computed}" } diff --git a/test/unit/watcher_test.rb b/test/unit/watcher_test.rb index afd722a6..958f7b93 100644 --- a/test/unit/watcher_test.rb +++ b/test/unit/watcher_test.rb @@ -6,4 +6,55 @@ class PollingWatcherTest < Spring::Test::WatcherTest def watcher_class Spring::Watcher::Polling end + + test "skips staleness checks if already stale" do + class << watcher + attr_reader :checked_when_stale_count + attr_reader :checked_when_not_stale_count + + def check_stale + @checked_when_stale_count = 0 unless defined? @checked_when_stale_count + @checked_when_not_stale_count = 0 unless defined? @checked_when_not_stale_count + + if stale? + @checked_when_stale_count += 1 + else + @checked_when_not_stale_count += 1 + end + + super + end + + # Wait for the poller thread to finish. + def join + @poller.join if @poller + end + end + + # Track when we're marked as stale. + on_stale_count = 0 + watcher.on_stale { on_stale_count += 1 } + + # Add a file to watch and start polling. + file = "#{@dir}/omg" + touch file, Time.now - 2.seconds + watcher.add file + watcher.start + assert watcher.running? + + # First touch bumps mtime and marks as stale. + touch file, Time.now - 1.second + Timeout.timeout(1) { watcher.join } + assert !watcher.running? + assert_equal 0, watcher.checked_when_stale_count + assert_equal 1, watcher.checked_when_not_stale_count + assert_equal 1, on_stale_count + + # Second touch skips mtime check because it's already stale. + touch file, Time.now + sleep 1 + assert_equal 0, watcher.checked_when_stale_count + assert_equal 1, watcher.checked_when_not_stale_count + assert_equal 1, on_stale_count + end end