lifecycle is HARD
This commit is contained in:
parent
94466a2da5
commit
2f4aa9dee1
|
@ -3,8 +3,10 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
|
||||
import Event, { mapEvent, filterEvent } from 'vs/base/common/event';
|
||||
import { fromEventEmitter } from 'vs/base/node/event';
|
||||
import { Server as IPCServer, Client as IPCClient, IServer, IClient, IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
|
||||
const Hello = 'ipc:hello';
|
||||
|
@ -21,16 +23,14 @@ class Protocol implements IMessagePassingProtocol {
|
|||
|
||||
private listener: IDisposable;
|
||||
|
||||
constructor(private sender: Sender, private receiver: NodeJS.EventEmitter) {}
|
||||
constructor(private sender: Sender, private onMessageEvent: Event<any>) {}
|
||||
|
||||
send(message: any): void {
|
||||
this.sender.send(Message, message);
|
||||
}
|
||||
|
||||
onMessage(callback: (message: any) => void): void {
|
||||
const cb = (_, m) => callback(m);
|
||||
this.receiver.on(Message, cb);
|
||||
this.listener = toDisposable(() => this.receiver.removeListener(Message, cb));
|
||||
this.listener = this.onMessageEvent(callback);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
@ -38,31 +38,46 @@ class Protocol implements IMessagePassingProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
interface IIPCEvent {
|
||||
event: any;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export class Server implements IServer, IDisposable {
|
||||
|
||||
private channels: { [name: string]: IChannel };
|
||||
private channels: { [name: string]: IChannel } = Object.create(null);
|
||||
|
||||
constructor(ipc: NodeJS.EventEmitter) {
|
||||
this.channels = Object.create(null);
|
||||
|
||||
ipc.on(Hello, ({ sender }) => {
|
||||
const protocol = new Protocol(sender, ipc);
|
||||
const ipcServer = new IPCServer(protocol);
|
||||
|
||||
Object.keys(this.channels)
|
||||
.forEach(name => ipcServer.registerChannel(name, this.channels[name]));
|
||||
|
||||
sender.once(Goodbye, () => {
|
||||
ipcServer.dispose();
|
||||
protocol.dispose();
|
||||
});
|
||||
});
|
||||
constructor(private ipc: NodeJS.EventEmitter) {
|
||||
ipc.on(Hello, ({ sender }) => this.onHello(sender));
|
||||
}
|
||||
|
||||
registerChannel(channelName: string, channel: IChannel): void {
|
||||
this.channels[channelName] = channel;
|
||||
}
|
||||
|
||||
private onHello(sender: any): void {
|
||||
const senderId = sender.getId();
|
||||
const onMessage = this.createScopedEvent(Message, senderId);
|
||||
const protocol = new Protocol(sender, onMessage);
|
||||
const ipcServer = new IPCServer(protocol);
|
||||
|
||||
Object.keys(this.channels)
|
||||
.forEach(name => ipcServer.registerChannel(name, this.channels[name]));
|
||||
|
||||
const onGoodbye = this.createScopedEvent(Goodbye, senderId);
|
||||
const listener = onGoodbye(() => {
|
||||
listener.dispose();
|
||||
ipcServer.dispose();
|
||||
protocol.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
private createScopedEvent(eventName: string, senderId: string) {
|
||||
const onRawMessageEvent = fromEventEmitter<IIPCEvent>(this.ipc, eventName, (event, message) => ({ event, message }));
|
||||
const onScopedRawMessageEvent = filterEvent<IIPCEvent>(onRawMessageEvent, ({ event }) => event.sender.getId() === senderId);
|
||||
return mapEvent<IIPCEvent,string>(onScopedRawMessageEvent, ({ message }) => message);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.channels = null;
|
||||
}
|
||||
|
@ -75,7 +90,9 @@ export class Client implements IClient, IDisposable {
|
|||
|
||||
constructor(private ipc: IPC) {
|
||||
ipc.send(Hello);
|
||||
this.protocol = new Protocol(ipc, ipc);
|
||||
|
||||
const receiverEvent = fromEventEmitter(ipc, Message, (_, message) => message);
|
||||
this.protocol = new Protocol(ipc, receiverEvent);
|
||||
this.ipcClient = new IPCClient(this.protocol);
|
||||
}
|
||||
|
||||
|
@ -86,5 +103,6 @@ export class Client implements IClient, IDisposable {
|
|||
dispose(): void {
|
||||
this.ipc.send(Goodbye);
|
||||
this.protocol = dispose(this.protocol);
|
||||
this.ipcClient = dispose(this.ipcClient);
|
||||
}
|
||||
}
|
|
@ -148,15 +148,17 @@ export class Server {
|
|||
}
|
||||
}
|
||||
|
||||
export class Client implements IClient {
|
||||
export class Client implements IClient, IDisposable {
|
||||
|
||||
private state: State;
|
||||
private activeRequests: Promise[];
|
||||
private bufferedRequests: IRequest[];
|
||||
private handlers: { [id: number]: IHandler; };
|
||||
private lastRequestId: number;
|
||||
|
||||
constructor(private protocol: IMessagePassingProtocol) {
|
||||
this.state = State.Uninitialized;
|
||||
this.activeRequests = [];
|
||||
this.bufferedRequests = [];
|
||||
this.handlers = Object.create(null);
|
||||
this.lastRequestId = 0;
|
||||
|
@ -179,11 +181,17 @@ export class Client implements IClient {
|
|||
}
|
||||
};
|
||||
|
||||
if (this.state === State.Uninitialized) {
|
||||
return this.bufferRequest(request);
|
||||
}
|
||||
const activeRequest = this.state === State.Uninitialized
|
||||
? this.bufferRequest(request)
|
||||
: this.doRequest(request);
|
||||
|
||||
return this.doRequest(request);
|
||||
this.activeRequests.push(activeRequest);
|
||||
|
||||
activeRequest
|
||||
.then(null, _ => null)
|
||||
.done(() => this.activeRequests = this.activeRequests.filter(i => i !== activeRequest));
|
||||
|
||||
return activeRequest;
|
||||
}
|
||||
|
||||
private doRequest(request: IRequest): Promise {
|
||||
|
@ -274,6 +282,11 @@ export class Client implements IClient {
|
|||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.activeRequests.forEach(r => r.cancel());
|
||||
this.activeRequests = [];
|
||||
}
|
||||
}
|
||||
|
||||
export function getDelayedChannel<T extends IChannel>(promise: TPromise<IChannel>): T {
|
||||
|
|
|
@ -5,32 +5,25 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import Event, {Emitter} from 'vs/base/common/event';
|
||||
import {IDisposable, dispose, toDisposable} from 'vs/base/common/lifecycle';
|
||||
import Event, {mapEvent} from 'vs/base/common/event';
|
||||
import {fromEventEmitter} from 'vs/base/node/event';
|
||||
import {IURLService} from 'vs/platform/url/common/url';
|
||||
import {app} from 'electron';
|
||||
|
||||
export class URLService implements IURLService, IDisposable {
|
||||
export class URLService implements IURLService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private _onOpenURL = new Emitter<string>();
|
||||
onOpenURL: Event<string> = this._onOpenURL.event;
|
||||
private disposables: IDisposable[] = [];
|
||||
onOpenURL: Event<string>;
|
||||
|
||||
constructor() {
|
||||
const handler = (e: Electron.Event, url: string) => {
|
||||
e.preventDefault();
|
||||
this._onOpenURL.fire(url);
|
||||
};
|
||||
const rawOnOpenUrl = fromEventEmitter(app, 'open-url', (event: Electron.Event, url: string) => ({ event, url }));
|
||||
|
||||
app.on('open-url', handler);
|
||||
this.disposables.push(toDisposable(() => app.removeListener('open-url', handler)));
|
||||
this.onOpenURL = mapEvent(rawOnOpenUrl, ({ event, url }) => {
|
||||
event.preventDefault();
|
||||
return url;
|
||||
});
|
||||
|
||||
// app.setAsDefaultProtocolClient('vscode');
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
|
@ -206,6 +206,8 @@ export class WorkbenchShell {
|
|||
}
|
||||
|
||||
private initServiceCollection(): [InstantiationService, ServiceCollection] {
|
||||
const disposables = new Disposables();
|
||||
|
||||
const sharedProcess = connectNet(process.env['VSCODE_SHARED_IPC_HOOK']);
|
||||
sharedProcess.done(service => {
|
||||
service.onClose(() => {
|
||||
|
@ -217,6 +219,7 @@ export class WorkbenchShell {
|
|||
}, errors.onUnexpectedError);
|
||||
|
||||
const mainProcessClient = new ElectronIPCClient(ipcRenderer);
|
||||
disposables.add(mainProcessClient);
|
||||
|
||||
const serviceCollection = new ServiceCollection();
|
||||
serviceCollection.set(IEventService, this.eventService);
|
||||
|
@ -224,7 +227,6 @@ export class WorkbenchShell {
|
|||
serviceCollection.set(IConfigurationService, this.configurationService);
|
||||
|
||||
const instantiationService = new InstantiationService(serviceCollection, true);
|
||||
const disposables = new Disposables();
|
||||
|
||||
this.windowService = instantiationService.createInstance(WindowService);
|
||||
serviceCollection.set(IWindowService, this.windowService);
|
||||
|
|
Loading…
Reference in a new issue