Skip to content

Commit

Permalink
Improve ability of downstream apps to control file watching
Browse files Browse the repository at this point in the history
Signed-off-by: Nigel Westbury <nigelipse@miegel.org>
  • Loading branch information
westbury committed Mar 15, 2021
1 parent be686ee commit 4f5f145
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 24 deletions.
63 changes: 44 additions & 19 deletions packages/filesystem/src/node/filesystem-backend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ContainerModule, interfaces } from 'inversify';
import { ConnectionHandler, JsonRpcConnectionHandler, ILogger } from '@theia/core/lib/common';
import { FileSystemWatcherServer, FileSystemWatcherService } from '../common/filesystem-watcher-protocol';
import { FileSystemWatcherServerClient } from './filesystem-watcher-client';
import { NsfwFileSystemWatcherService } from './nsfw-watcher/nsfw-filesystem-service';
import { NsfwFileSystemWatcherService, NsfwFileSystemWatcherServerOptions } from './nsfw-watcher/nsfw-filesystem-service';
import { MessagingService } from '@theia/core/lib/node/messaging/messaging-service';
import { NodeFileUploadService } from './node-file-upload-service';
import { NsfwOptions } from './nsfw-watcher/nsfw-options';
Expand All @@ -36,6 +36,19 @@ import { FileSystemWatcherServiceDispatcher } from './filesystem-watcher-dispatc
const SINGLE_THREADED = process.argv.indexOf('--no-cluster') !== -1;
const NSFW_WATCHER_VERBOSE = process.argv.indexOf('--nsfw-watcher-verbose') !== -1;

export interface FileWatcherSingleProcessOptions {
processType: 'single';
serverOptions: NsfwFileSystemWatcherServerOptions;
}

export interface FileWatcherMultiProcessOptions {
processType: 'multi';
entryPoint: string;
}

export const FileWatcherProcessOptions = Symbol('FileWatcherProcessOptions');
type FileWatcherProcessOptions = FileWatcherSingleProcessOptions | FileWatcherMultiProcessOptions;

export function bindFileSystemWatcherServer(bind: interfaces.Bind, { singleThreaded }: { singleThreaded: boolean } = { singleThreaded: SINGLE_THREADED }): void {
bind<NsfwOptions>(NsfwOptions).toConstantValue({});

Expand All @@ -45,29 +58,41 @@ export function bindFileSystemWatcherServer(bind: interfaces.Bind, { singleThrea
bind(FileSystemWatcherServer).toService(FileSystemWatcherServerClient);

if (singleThreaded) {
// Bind and run the watch server in the current process:
bind<FileSystemWatcherService>(FileSystemWatcherService).toDynamicValue(ctx => {
bind<FileWatcherProcessOptions>(FileWatcherProcessOptions).toDynamicValue(ctx => {
const logger = ctx.container.get<ILogger>(ILogger);
const nsfwOptions = ctx.container.get<NsfwOptions>(NsfwOptions);
const dispatcher = ctx.container.get<FileSystemWatcherServiceDispatcher>(FileSystemWatcherServiceDispatcher);
const server = new NsfwFileSystemWatcherService({
nsfwOptions,
verbose: NSFW_WATCHER_VERBOSE,
info: (message, ...args) => logger.info(message, ...args),
error: (message, ...args) => logger.error(message, ...args)
});
server.setClient(dispatcher);
return server;
return {
processType: 'single',
serverOptions: {
nsfwOptions,
verbose: NSFW_WATCHER_VERBOSE,
info: (message, ...args) => logger.info(message, ...args),
error: (message, ...args) => logger.error(message, ...args)
}
} as FileWatcherSingleProcessOptions;
}).inSingletonScope();
} else {
// Run the watch server in a child process.
// Bind to a proxy forwarding calls to the child process.
bind<FileSystemWatcherService>(FileSystemWatcherService).toDynamicValue(ctx => {
bind<FileWatcherProcessOptions>(FileWatcherProcessOptions).toConstantValue({
processType: 'multi',
entryPoint: path.resolve(__dirname, 'nsfw-watcher')
});
}

bind<FileSystemWatcherService>(FileSystemWatcherService).toDynamicValue(ctx => {
const watcherOptions = ctx.container.get<FileWatcherProcessOptions>(FileWatcherProcessOptions);
const dispatcher = ctx.container.get<FileSystemWatcherServiceDispatcher>(FileSystemWatcherServiceDispatcher);
if (watcherOptions.processType === 'single') {
// Bind and run the watch server in the current process:
const server = new NsfwFileSystemWatcherService(watcherOptions.serverOptions);
server.setClient(dispatcher);
return server;
} else {
// Run the watch server in a child process.
// Bind to a proxy forwarding calls to the child process.
const serverName = 'nsfw-watcher';
const logger = ctx.container.get<ILogger>(ILogger);
const nsfwOptions = ctx.container.get<NsfwOptions>(NsfwOptions);
const ipcConnectionProvider = ctx.container.get<IPCConnectionProvider>(IPCConnectionProvider);
const dispatcher = ctx.container.get<FileSystemWatcherServiceDispatcher>(FileSystemWatcherServiceDispatcher);
const proxyFactory = new JsonRpcProxyFactory<FileSystemWatcherService>();
const serverProxy = proxyFactory.createProxy();
// We need to call `.setClient` before listening, else the JSON-RPC calls won't go through.
Expand All @@ -80,7 +105,7 @@ export function bindFileSystemWatcherServer(bind: interfaces.Bind, { singleThrea
}
ipcConnectionProvider.listen({
serverName,
entryPoint: path.resolve(__dirname, serverName),
entryPoint: watcherOptions.entryPoint,
errorHandler: new ConnectionErrorHandler({
serverName,
logger,
Expand All @@ -89,8 +114,8 @@ export function bindFileSystemWatcherServer(bind: interfaces.Bind, { singleThrea
args,
}, connection => proxyFactory.listen(connection));
return serverProxy;
}).inSingletonScope();
}
}
}).inSingletonScope();
}

export default new ContainerModule(bind => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,11 +422,7 @@ export class NsfwFileSystemWatcherService implements FileSystemWatcherService {
let watcher = this.watchers.get(watcherKey);
if (watcher === undefined) {
const fsPath = FileUri.fsPath(uri);
const watcherOptions: NsfwWatcherOptions = {
ignored: resolvedOptions.ignored
.map(pattern => new Minimatch(pattern, { dot: true })),
};
watcher = new NsfwWatcher(clientId, fsPath, watcherOptions, this.options, this.maybeClient);
watcher = this.createWatcher(clientId, fsPath, resolvedOptions);
watcher.whenDisposed.then(() => this.watchers.delete(watcherKey));
this.watchers.set(watcherKey, watcher);
} else {
Expand All @@ -438,6 +434,14 @@ export class NsfwFileSystemWatcherService implements FileSystemWatcherService {
return watcherId;
}

protected createWatcher(clientId: number, fsPath: string, resolvedOptions: WatchOptions): NsfwWatcher {
const watcherOptions: NsfwWatcherOptions = {
ignored: resolvedOptions.ignored
.map(pattern => new Minimatch(pattern, { dot: true })),
};
return new NsfwWatcher(clientId, fsPath, watcherOptions, this.options, this.maybeClient);
}

async unwatchFileChanges(watcherId: number): Promise<void> {
const handle = this.watcherHandles.get(watcherId);
if (handle === undefined) {
Expand Down

0 comments on commit 4f5f145

Please sign in to comment.