Fix PathWatcher failures

Now when a file is removed, we always remove its subscriptions and its kevent.
This commit is contained in:
Corey Johnson & Nathan Sobo
2012-11-19 13:15:35 -07:00
parent 5da44b9eef
commit f9563f5e55
5 changed files with 42 additions and 39 deletions

View File

@@ -137,23 +137,10 @@ static NSMutableArray *gPathWatchers;
}
if (callbacks.count == 0) {
bool fileExists = access([path fileSystemRepresentation], F_OK) != -1;
if (fileExists) {
[self removeKeventForPath:path];
}
[self removeKeventForPath:path];
[_callbacksByPath removeObjectForKey:path];
}
}
else {
NSString *message = [NSString stringWithFormat:@"Trying to unwatch %@, which we aren't watching", path];
NSLog(@"WARNING: %@", message);
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, nil];
if (error) {
NSError *e = [NSError errorWithDomain:@"PathWatcher" code:0 userInfo:userInfo];
*error = e;
}
return;
}
}
}
@@ -162,6 +149,7 @@ static NSMutableArray *gPathWatchers;
@synchronized(self) {
if ([_fileDescriptorsByPath objectForKey:path]) {
NSLog(@"we already have a kevent");
return YES;
}
@@ -209,7 +197,6 @@ static NSMutableArray *gPathWatchers;
- (void)changePath:(NSString *)path toNewPath:(NSString *)newPath {
@synchronized(self) {
NSDictionary *callbacks = [NSDictionary dictionaryWithDictionary:[_callbacksByPath objectForKey:path]];
[self removeKeventForPath:path];
[self unwatchPath:path callbackId:nil error:nil];
for (NSString *callbackId in [callbacks allKeys]) {
[self watchPath:newPath callback:[callbacks objectForKey:callbackId] callbackId:callbackId];
@@ -243,7 +230,6 @@ static NSMutableArray *gPathWatchers;
}
else if (event.fflags & NOTE_DELETE) {
eventFlag = @"remove";
[self removeKeventForPath:path];
}
else if (event.fflags & NOTE_RENAME) {
eventFlag = @"move";
@@ -256,6 +242,7 @@ static NSMutableArray *gPathWatchers;
continue;
}
}
NSDictionary *callbacks;
@synchronized(self) {
@@ -269,9 +256,13 @@ static NSMutableArray *gPathWatchers;
}
});
if (event.fflags & NOTE_RENAME) {
if ([eventFlag isEqual:@"move"]) {
[self changePath:path toNewPath:newPath];
}
if ([eventFlag isEqual:@"remove"]) {
[self unwatchPath:path callbackId:nil error:nil];
}
[path release];
}

View File

@@ -86,7 +86,9 @@ describe 'Buffer', ->
buffer = new Buffer(path).retain()
afterEach ->
fs.remove(path)
buffer.release()
buffer = null
fs.remove(path) if fs.exists(path)
it "does not trigger a contents-change event when Atom modifies the file", ->
buffer.insert([0,0], "HELLO!")
@@ -309,7 +311,7 @@ describe 'Buffer', ->
describe ".save()", ->
saveBuffer = null
afterEach ->
saveBuffer.release()

View File

@@ -87,6 +87,7 @@ describe 'File', ->
describe "when a file is deleted and the recreated within a small amount of time (git sometimes does this)", ->
it "triggers a contents change event if the contents change", ->
jasmine.unspy(File.prototype, 'detectResurrectionAfterDelay')
jasmine.unspy(window, "setTimeout")
changeHandler = jasmine.createSpy("file changed")
@@ -94,16 +95,23 @@ describe 'File', ->
file.on 'contents-change', changeHandler
file.on 'remove', removeHandler
fs.remove(path)
fs.write(path, "HE HAS RISEN!")
expect(changeHandler).not.toHaveBeenCalled()
waitsFor "change event", ->
changeHandler.callCount > 0
fs.remove(path)
expect(changeHandler).not.toHaveBeenCalled()
waits 20
runs ->
fs.write(path, "HE HAS RISEN!")
expect(changeHandler).not.toHaveBeenCalled()
waitsFor "resurrection change event", ->
changeHandler.callCount == 1
runs ->
expect(removeHandler).not.toHaveBeenCalled()
fs.write(path, "Hallelujah!")
changeHandler.reset()
waitsFor "change event", ->
waitsFor "post-resurrection change event", ->
changeHandler.callCount > 0

View File

@@ -27,6 +27,7 @@ beforeEach ->
spyOn(RootView.prototype, 'updateWindowTitle').andCallFake ->
spyOn(window, "setTimeout").andCallFake window.fakeSetTimeout
spyOn(window, "clearTimeout").andCallFake window.fakeClearTimeout
spyOn(File.prototype, "detectResurrectionAfterDelay").andCallFake -> @detectResurrection()
afterEach ->
delete window.rootView if window.rootView

View File

@@ -31,23 +31,9 @@ class File
afterUnsubscribe: ->
@unsubscribeFromNativeChangeEvents() if @subscriptionCount() == 0
subscribeToNativeChangeEvents: ->
@watchId = $native.watchPath @path, (eventType, path) =>
@handleNativeChangeEvent(eventType, path)
handleNativeChangeEvent: (eventType, path) ->
console.log eventType
if eventType is "remove"
@unsubscribeFromNativeChangeEvents()
detectResurrection = =>
if @exists()
@subscribeToNativeChangeEvents()
@handleNativeChangeEvent("contents-change", path)
else
@trigger "remove"
@off()
_.delay detectResurrection, 50
@detectResurrectionAfterDelay()
else if eventType is "move"
@setPath(path)
@trigger "move"
@@ -58,6 +44,21 @@ class File
@md5 = newMd5
@trigger 'contents-change'
detectResurrectionAfterDelay: ->
_.delay (=> @detectResurrection()), 50
detectResurrection: ->
if @exists()
@subscribeToNativeChangeEvents()
@handleNativeChangeEvent("contents-change", @getPath())
else
@trigger "remove"
@off()
subscribeToNativeChangeEvents: ->
@watchId = $native.watchPath @path, (eventType, path) =>
@handleNativeChangeEvent(eventType, path)
unsubscribeFromNativeChangeEvents: ->
$native.unwatchPath(@path, @watchId)