-
Notifications
You must be signed in to change notification settings - Fork 21.6k
Commit
listen is calling us from its own thread, we need to synchronize reads and writes to this flag.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,6 +1,8 @@ | ||||||||||||||||||
require 'listen' | ||||||||||||||||||
require 'set' | ||||||||||||||||||
require 'pathname' | ||||||||||||||||||
require 'thread' | ||||||||||||||||||
require 'concurrent/atomic/atomic_boolean' | ||||||||||||||||||
|
||||||||||||||||||
module ActiveSupport | ||||||||||||||||||
class FileEventedUpdateChecker #:nodoc: all | ||||||||||||||||||
|
@@ -14,22 +16,24 @@ def initialize(files, dirs = {}, &block) | |||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
@block = block | ||||||||||||||||||
@updated = false | ||||||||||||||||||
@updated = Concurrent::AtomicBoolean.new(false) | ||||||||||||||||||
@lcsp = @ph.longest_common_subpath(@dirs.keys) | ||||||||||||||||||
|
||||||||||||||||||
if (dtw = directories_to_watch).any? | ||||||||||||||||||
Listen.to(*dtw, &method(:changed)).start | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
@mutex = Mutex.new | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
def updated? | ||||||||||||||||||
@updated | ||||||||||||||||||
@updated.true? | ||||||||||||||||||
end | ||||||||||||||||||
|
||||||||||||||||||
def execute | ||||||||||||||||||
@block.call | ||||||||||||||||||
ensure | ||||||||||||||||||
@updated = false | ||||||||||||||||||
@updated.make_false | ||||||||||||||||||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
fxn
Author
Member
|
def execute | |
@last_watched = watched | |
@last_update_at = updated_at(@last_watched) | |
@block.call | |
ensure | |
@watched = nil | |
@updated_at = nil | |
end |
@josevalim do you remember if there was a reason to do it that way?
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
fxn
Nov 21, 2015
Author
Member
After thinking about it, I'd say in the original monitor the order wouldn't matter, since the flag won't be checked until the next run. I wonder about the ensure
block though, it is not something you normally write without a justification, but cannot see a reason not to reset the ivars before the callback invocation.
In our case things are async. Yes, unless I am missing something, I think the flag should be better updated before the callback invocation.
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
fxn
Nov 21, 2015
Author
Member
Revised in 4596c1a. If there's anything overlooked let's revert, but looks right to me AFAICT.
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
matthewd
Nov 21, 2015
Member
AFAICS, this should be fine as unless updated?; @updated.make_true if (modified + added + removed).any? { |f| watching?(f) }; end
, without the mutex.
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
fxn
Nov 21, 2015
Author
Member
I think there would be a race condition without the mutex.
Let's suppose @updated
is false. Two parallel threads call the method and pass the predicate check, so both enter the body of unless
. The thread that gets to run first there sets the flag to true
, but the second one sets it to false
. In that scenario the object misses there was an update.
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
fxposter
Nov 22, 2015
Contributor
@fxn, I agree with @matthewd, actually. Your example is incorrect in this part: "but the second one sets it to false
". @matthewd proposes not toset it to false in changed
method ever. IE: if something is changed - set to true, if not - do nothing.
There is a race condition, but if we end up with setting the @updated
variable only to true here - it doesn't matter. IE: at least one thread will set it to true, and if all other methods will set it to true again - we don't care.
The method can be simplified to:
@updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
And the unless updated?
can be added only to skip all those watching?
checks (ie: it is actually not essential for the method to work). I tried to think about the implications of having not synchronized read and write of the variable and in this case I couldn't find any problems with it.
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
This comment has been minimized.
Sorry, something went wrong.
@fxn shouldn't this happen before we execute the block? If something changes while we're executing, we need to do another reload next time.