Skip to content

Commit 6217245

Browse files
committed
Clean up implementation of the proxy protocol
1 parent 5553acb commit 6217245

1 file changed

Lines changed: 96 additions & 72 deletions

File tree

src/vs/workbench/services/extensions/node/ipcRemoteCom.ts

Lines changed: 96 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ import errors = require('vs/base/common/errors');
1010
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
1111
import { LazyPromise } from "vs/workbench/services/extensions/node/lazyPromise";
1212

13-
interface IRPCFunc {
14-
(rpcId: string, method: string, args: any[]): winjs.TPromise<any>;
15-
}
16-
1713
let lastMessageId = 0;
1814
const pendingRPCReplies: { [msgId: string]: LazyPromise; } = {};
1915

@@ -41,22 +37,6 @@ class MessageFactory {
4137
}
4238
}
4339

44-
function createRPC(serializeAndSend: (value: string) => void): IRPCFunc {
45-
46-
return function rpc(rpcId: string, method: string, args: any[]): winjs.TPromise<any> {
47-
let req = String(++lastMessageId);
48-
let result = new LazyPromise(() => {
49-
serializeAndSend(MessageFactory.cancel(req));
50-
});
51-
52-
pendingRPCReplies[req] = result;
53-
54-
serializeAndSend(MessageFactory.request(req, rpcId, method, args));
55-
56-
return result;
57-
};
58-
}
59-
6040
export interface IManyHandler {
6141
handle(rpcId: string, method: string, args: any[]): any;
6242
}
@@ -66,20 +46,77 @@ export interface IRemoteCom {
6646
setManyHandler(handler: IManyHandler): void;
6747
}
6848

69-
export function createProxyProtocol(protocol: IMessagePassingProtocol): IRemoteCom {
70-
let rpc = createRPC(sendDelayed);
71-
let bigHandler: IManyHandler = null;
72-
let invokedHandlers: { [req: string]: winjs.TPromise<any>; } = Object.create(null);
73-
let messagesToSend: string[] = [];
49+
/**
50+
* Sends/Receives multiple messages in one go:
51+
* - multiple messages to be sent from one stack get sent in bulk at `process.nextTick`.
52+
* - each incoming message is handled in a separate `process.nextTick`.
53+
*/
54+
class RPCMultiplexer {
55+
56+
private readonly _protocol: IMessagePassingProtocol;
57+
private readonly _onMessage: (msg: string) => void;
58+
private readonly _receiveOneMessageBound: () => void;
59+
private readonly _sendAccumulatedBound: () => void;
60+
61+
private _messagesToSend: string[];
62+
private _messagesToReceive: string[];
63+
64+
constructor(protocol: IMessagePassingProtocol, onMessage: (msg: string) => void) {
65+
this._protocol = protocol;
66+
this._onMessage = onMessage;
67+
this._receiveOneMessageBound = this._receiveOneMessage.bind(this);
68+
this._sendAccumulatedBound = this._sendAccumulated.bind(this);
69+
70+
this._messagesToSend = [];
71+
this._messagesToReceive = [];
72+
73+
this._protocol.onMessage(data => {
74+
// console.log('RECEIVED ' + rawmsg.length + ' MESSAGES.');
75+
if (this._messagesToReceive.length === 0) {
76+
process.nextTick(this._receiveOneMessageBound);
77+
}
7478

75-
let messagesToReceive: string[] = [];
76-
let receiveOneMessage = () => {
77-
let rawmsg = messagesToReceive.shift();
79+
this._messagesToReceive = this._messagesToReceive.concat(data);
80+
});
81+
}
7882

79-
if (messagesToReceive.length > 0) {
80-
process.nextTick(receiveOneMessage);
83+
private _receiveOneMessage(): void {
84+
const rawmsg = this._messagesToReceive.shift();
85+
86+
if (this._messagesToReceive.length > 0) {
87+
process.nextTick(this._receiveOneMessageBound);
8188
}
8289

90+
this._onMessage(rawmsg);
91+
}
92+
93+
private _sendAccumulated(): void {
94+
const tmp = this._messagesToSend;
95+
this._messagesToSend = [];
96+
this._protocol.send(tmp);
97+
}
98+
99+
public send(msg: string): void {
100+
if (this._messagesToSend.length === 0) {
101+
process.nextTick(this._sendAccumulatedBound);
102+
}
103+
this._messagesToSend.push(msg);
104+
}
105+
}
106+
107+
export class RPCManager implements IRemoteCom {
108+
109+
private _bigHandler: IManyHandler;
110+
private readonly _invokedHandlers: { [req: string]: winjs.TPromise<any>; };
111+
private readonly _multiplexor: RPCMultiplexer;
112+
113+
constructor(protocol: IMessagePassingProtocol) {
114+
this._bigHandler = null;
115+
this._invokedHandlers = Object.create(null);
116+
this._multiplexor = new RPCMultiplexer(protocol, (msg) => this._receiveOneMessage(msg));
117+
}
118+
119+
private _receiveOneMessage(rawmsg: string): void {
83120
let msg = marshalling.parse(rawmsg);
84121

85122
if (msg.seq) {
@@ -107,8 +144,8 @@ export function createProxyProtocol(protocol: IMessagePassingProtocol): IRemoteC
107144
}
108145

109146
if (msg.cancel) {
110-
if (invokedHandlers[msg.cancel]) {
111-
invokedHandlers[msg.cancel].cancel();
147+
if (this._invokedHandlers[msg.cancel]) {
148+
this._invokedHandlers[msg.cancel].cancel();
112149
}
113150
return;
114151
}
@@ -120,62 +157,49 @@ export function createProxyProtocol(protocol: IMessagePassingProtocol): IRemoteC
120157

121158
let rpcId = msg.rpcId;
122159

123-
if (!bigHandler) {
160+
if (!this._bigHandler) {
124161
throw new Error('got message before big handler attached!');
125162
}
126163

127164
let req = msg.req;
128165

129-
invokedHandlers[req] = invokeHandler(rpcId, msg.method, msg.args);
166+
this._invokedHandlers[req] = this._invokeHandler(rpcId, msg.method, msg.args);
130167

131-
invokedHandlers[req].then((r) => {
132-
delete invokedHandlers[req];
133-
sendDelayed(MessageFactory.replyOK(req, r));
168+
this._invokedHandlers[req].then((r) => {
169+
delete this._invokedHandlers[req];
170+
this._multiplexor.send(MessageFactory.replyOK(req, r));
134171
}, (err) => {
135-
delete invokedHandlers[req];
136-
sendDelayed(MessageFactory.replyErr(req, err));
172+
delete this._invokedHandlers[req];
173+
this._multiplexor.send(MessageFactory.replyErr(req, err));
137174
});
138-
};
175+
}
139176

140-
protocol.onMessage(data => {
141-
// console.log('RECEIVED ' + rawmsg.length + ' MESSAGES.');
142-
if (messagesToReceive.length === 0) {
143-
process.nextTick(receiveOneMessage);
177+
private _invokeHandler(rpcId: string, method: string, args: any[]): winjs.TPromise<any> {
178+
try {
179+
return winjs.TPromise.as(this._bigHandler.handle(rpcId, method, args));
180+
} catch (err) {
181+
return winjs.TPromise.wrapError(err);
144182
}
183+
}
145184

146-
messagesToReceive = messagesToReceive.concat(data);
147-
});
148-
149-
let r: IRemoteCom = {
150-
callOnRemote: rpc,
151-
setManyHandler: (_bigHandler: IManyHandler): void => {
152-
bigHandler = _bigHandler;
153-
}
154-
};
185+
public callOnRemote(proxyId: string, path: string, args: any[]): winjs.TPromise<any> {
186+
let req = String(++lastMessageId);
187+
let result = new LazyPromise(() => {
188+
this._multiplexor.send(MessageFactory.cancel(req));
189+
});
155190

156-
function sendAccumulated(): void {
157-
let tmp = messagesToSend;
158-
messagesToSend = [];
191+
pendingRPCReplies[req] = result;
159192

160-
// console.log('SENDING ' + tmp.length + ' MESSAGES.');
161-
protocol.send(tmp);
162-
}
193+
this._multiplexor.send(MessageFactory.request(req, proxyId, path, args));
163194

164-
function sendDelayed(value: string): void {
165-
if (messagesToSend.length === 0) {
166-
process.nextTick(sendAccumulated);
167-
}
168-
messagesToSend.push(value);
195+
return result;
169196
}
170197

171-
function invokeHandler(rpcId: string, method: string, args: any[]): winjs.TPromise<any> {
172-
try {
173-
return winjs.TPromise.as(bigHandler.handle(rpcId, method, args));
174-
} catch (err) {
175-
return winjs.TPromise.wrapError(err);
176-
}
198+
public setManyHandler(handler: IManyHandler): void {
199+
this._bigHandler = handler;
177200
}
178-
179-
return r;
180201
}
181202

203+
export function createProxyProtocol(protocol: IMessagePassingProtocol): IRemoteCom {
204+
return new RPCManager(protocol);
205+
}

0 commit comments

Comments
 (0)