Merge pull request #47774 from zzak/fix-race-condition-in-evented-file-update-checker
Fix race condition in evented file update checker
This commit is contained in:
commit
3d9edd4ced
@ -45,6 +45,10 @@ def initialize(files, dirs = {}, &block)
|
||||
ObjectSpace.define_finalizer(self, @core.finalizer)
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<ActiveSupport::EventedFileUpdateChecker:#{object_id} @files=#{@core.files.to_a.inspect}"
|
||||
end
|
||||
|
||||
def updated?
|
||||
if @core.restart?
|
||||
@core.thread_safely(&:restart)
|
||||
@ -68,7 +72,7 @@ def execute_if_updated
|
||||
end
|
||||
|
||||
class Core
|
||||
attr_reader :updated
|
||||
attr_reader :updated, :files
|
||||
|
||||
def initialize(files, dirs)
|
||||
@files = files.map { |file| Pathname(file).expand_path }.to_set
|
||||
@ -86,6 +90,10 @@ def initialize(files, dirs)
|
||||
@mutex = Mutex.new
|
||||
|
||||
start
|
||||
# inotify / FSEvents file descriptors are inherited on fork, so
|
||||
# we need to reopen them otherwise only the parent or the child
|
||||
# will be notified.
|
||||
# FIXME: this callback is keeping a reference on the instance
|
||||
@after_fork = ActiveSupport::ForkTracker.after_fork { start }
|
||||
end
|
||||
|
||||
@ -107,6 +115,11 @@ def start
|
||||
@dtw, @missing = [*@dtw, *@missing].partition(&:exist?)
|
||||
@listener = @dtw.any? ? Listen.to(*@dtw, &method(:changed)) : nil
|
||||
@listener&.start
|
||||
|
||||
# Wait for the listener to be ready to avoid race conditions
|
||||
# Unfortunately this isn't quite enough on macOS because the Darwin backend
|
||||
# has an extra private thread we can't wait on.
|
||||
@listener&.wait_for_state(:processing_events)
|
||||
end
|
||||
|
||||
def stop
|
||||
|
@ -26,7 +26,7 @@ def teardown
|
||||
end
|
||||
|
||||
def wait
|
||||
sleep 1
|
||||
sleep 0.5
|
||||
end
|
||||
|
||||
def mkdir(dirs)
|
||||
@ -60,6 +60,10 @@ def rm_f(files)
|
||||
pid = fork do
|
||||
assert_not_predicate checker, :updated?
|
||||
|
||||
# The listen gem start multiple background threads that need to reach a ready state.
|
||||
# Unfortunately, it doesn't look like there is a clean way to block until they are ready.
|
||||
wait
|
||||
|
||||
# Fork is booted, ready for file to be touched
|
||||
# notify parent process.
|
||||
boot_writer.write("booted")
|
||||
@ -70,7 +74,7 @@ def rm_f(files)
|
||||
|
||||
assert_predicate checker, :updated?
|
||||
rescue Exception => ex
|
||||
result_writer.write(ex.class.name)
|
||||
result_writer.write("#{ex.class.name}: #{ex.message}")
|
||||
raise
|
||||
ensure
|
||||
result_writer.close
|
||||
|
Loading…
Reference in New Issue
Block a user