From 523f789333b3bd361cb8ab7a76e6e01985394fc6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 31 Aug 2018 16:30:07 +0200 Subject: [PATCH] fix #40533 --- .../files/electron-browser/fileService.ts | 115 ++++++++++-------- 1 file changed, 62 insertions(+), 53 deletions(-) diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index ac357fc3700..4b4bfa4f724 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -99,7 +99,7 @@ export class FileService extends Disposable implements IFileService { get onDidChangeFileSystemProviderRegistrations(): Event { return this._onDidChangeFileSystemProviderRegistrations.event; } private activeWorkspaceFileChangeWatcher: IDisposable; - private activeFileChangesWatchers: ResourceMap; + private activeFileChangesWatchers: ResourceMap<{ unwatch: Function, count: number }>; private fileChangesWatchDelayer: ThrottledDelayer; private undeliveredRawFileChangesEvents: IRawFileChange[]; @@ -117,7 +117,7 @@ export class FileService extends Disposable implements IFileService { ) { super(); - this.activeFileChangesWatchers = new ResourceMap(); + this.activeFileChangesWatchers = new ResourceMap<{ unwatch: Function, count: number }>(); this.fileChangesWatchDelayer = new ThrottledDelayer(FileService.FS_EVENT_DELAY); this.undeliveredRawFileChangesEvents = []; @@ -991,58 +991,67 @@ export class FileService extends Disposable implements IFileService { watchFileChanges(resource: uri): void { assert.ok(resource && resource.scheme === Schemas.file, `Invalid resource for watching: ${resource}`); - // Create or get watcher for provided path - let watcher = this.activeFileChangesWatchers.get(resource); - if (!watcher) { - const fsPath = resource.fsPath; - const fsName = paths.basename(resource.fsPath); - - watcher = extfs.watch(fsPath, (eventType: string, filename: string) => { - const renamedOrDeleted = ((filename && filename !== fsName) || eventType === 'rename'); - - // The file was either deleted or renamed. Many tools apply changes to files in an - // atomic way ("Atomic Save") by first renaming the file to a temporary name and then - // renaming it back to the original name. Our watcher will detect this as a rename - // and then stops to work on Mac and Linux because the watcher is applied to the - // inode and not the name. The fix is to detect this case and trying to watch the file - // again after a certain delay. - // In addition, we send out a delete event if after a timeout we detect that the file - // does indeed not exist anymore. - if (renamedOrDeleted) { - - // Very important to dispose the watcher which now points to a stale inode - this.unwatchFileChanges(resource); - - // Wait a bit and try to install watcher again, assuming that the file was renamed quickly ("Atomic Save") - setTimeout(() => { - this.existsFile(resource).done(exists => { - - // File still exists, so reapply the watcher - if (exists) { - this.watchFileChanges(resource); - } + // Check for existing watcher first + const entry = this.activeFileChangesWatchers.get(resource); + if (entry) { + entry.count += 1; - // File seems to be really gone, so emit a deleted event - else { - this.onRawFileChange({ - type: FileChangeType.DELETED, - path: fsPath - }); - } - }); - }, FileService.FS_REWATCH_DELAY); - } + return; + } - // Handle raw file change - this.onRawFileChange({ - type: FileChangeType.UPDATED, - path: fsPath - }); - }, (error: string) => this.handleError(error)); + // Create or get watcher for provided path + const fsPath = resource.fsPath; + const fsName = paths.basename(resource.fsPath); + + const watcher = extfs.watch(fsPath, (eventType: string, filename: string) => { + const renamedOrDeleted = ((filename && filename !== fsName) || eventType === 'rename'); + + // The file was either deleted or renamed. Many tools apply changes to files in an + // atomic way ("Atomic Save") by first renaming the file to a temporary name and then + // renaming it back to the original name. Our watcher will detect this as a rename + // and then stops to work on Mac and Linux because the watcher is applied to the + // inode and not the name. The fix is to detect this case and trying to watch the file + // again after a certain delay. + // In addition, we send out a delete event if after a timeout we detect that the file + // does indeed not exist anymore. + if (renamedOrDeleted) { + + // Very important to dispose the watcher which now points to a stale inode + this.unwatchFileChanges(resource); + + // Wait a bit and try to install watcher again, assuming that the file was renamed quickly ("Atomic Save") + setTimeout(() => { + this.existsFile(resource).done(exists => { + + // File still exists, so reapply the watcher + if (exists) { + this.watchFileChanges(resource); + } - if (watcher) { - this.activeFileChangesWatchers.set(resource, watcher); + // File seems to be really gone, so emit a deleted event + else { + this.onRawFileChange({ + type: FileChangeType.DELETED, + path: fsPath + }); + } + }); + }, FileService.FS_REWATCH_DELAY); } + + // Handle raw file change + this.onRawFileChange({ + type: FileChangeType.UPDATED, + path: fsPath + }); + }, (error: string) => this.handleError(error)); + + // Remember in map + if (watcher) { + this.activeFileChangesWatchers.set(resource, { + count: 1, + unwatch: () => watcher.close() + }); } } @@ -1079,8 +1088,8 @@ export class FileService extends Disposable implements IFileService { unwatchFileChanges(resource: uri): void { const watcher = this.activeFileChangesWatchers.get(resource); - if (watcher) { - watcher.close(); + if (watcher && --watcher.count === 0) { + watcher.unwatch(); this.activeFileChangesWatchers.delete(resource); } } @@ -1093,7 +1102,7 @@ export class FileService extends Disposable implements IFileService { this.activeWorkspaceFileChangeWatcher = null; } - this.activeFileChangesWatchers.forEach(watcher => watcher.close()); + this.activeFileChangesWatchers.forEach(watcher => watcher.unwatch()); this.activeFileChangesWatchers.clear(); } } -- GitLab