Listen on fork in EventedFileUpdateChecker
The Listen gem does not notify across process forks, and so forked processes must re-create their listeners. `EventedFileUpdateChecker` handled this in `updated?` by checking if `Process.pid` was different. Because file updates may have occurred after the start of the fork but before the call to `updated?`, `updated?` would always return true in this case. However, the above approach can result in unnecessary application reloading, particularly when using Spring. That, in turn, can cause unexpected and problematic behavior: #39431, #37591. This commit changes listener re-creation to occur immediately after fork, and changes `updated?` to return true only when necessary.
This commit is contained in:
parent
f8b9150f1c
commit
eba1534939
|
@ -4,6 +4,7 @@ require "set"
|
|||
require "pathname"
|
||||
require "concurrent/atomic/atomic_boolean"
|
||||
require "listen"
|
||||
require "active_support/fork_tracker"
|
||||
|
||||
module ActiveSupport
|
||||
# Allows you to "listen" to changes in a file system.
|
||||
|
@ -40,20 +41,11 @@ module ActiveSupport
|
|||
end
|
||||
|
||||
@block = block
|
||||
@pid = Process.pid
|
||||
@core = Core.new(files, dirs)
|
||||
ObjectSpace.define_finalizer(self, @core.finalizer)
|
||||
end
|
||||
|
||||
def updated?
|
||||
@core.mutex.synchronize do
|
||||
if @pid != Process.pid
|
||||
@core.start
|
||||
@pid = Process.pid
|
||||
@core.updated.make_true
|
||||
end
|
||||
end
|
||||
|
||||
if @core.restart?
|
||||
@core.thread_safely(&:restart)
|
||||
@core.updated.make_true
|
||||
|
@ -76,7 +68,7 @@ module ActiveSupport
|
|||
end
|
||||
|
||||
class Core
|
||||
attr_reader :updated, :mutex
|
||||
attr_reader :updated
|
||||
|
||||
def initialize(files, dirs)
|
||||
@files = files.map { |file| Pathname(file).expand_path }.to_set
|
||||
|
@ -94,10 +86,14 @@ module ActiveSupport
|
|||
@mutex = Mutex.new
|
||||
|
||||
start
|
||||
@after_fork = ActiveSupport::ForkTracker.after_fork { start }
|
||||
end
|
||||
|
||||
def finalizer
|
||||
proc { stop }
|
||||
proc do
|
||||
stop
|
||||
ActiveSupport::ForkTracker.unregister(@after_fork)
|
||||
end
|
||||
end
|
||||
|
||||
def thread_safely
|
||||
|
|
|
@ -47,10 +47,6 @@ class EventedFileUpdateCheckerTest < ActiveSupport::TestCase
|
|||
touch_reader, touch_writer = IO.pipe
|
||||
|
||||
pid = fork do
|
||||
assert_predicate checker, :updated?
|
||||
|
||||
# Clear previous check value.
|
||||
checker.execute
|
||||
assert_not_predicate checker, :updated?
|
||||
|
||||
# Fork is booted, ready for file to be touched
|
||||
|
|
Loading…
Reference in New Issue