Skip to content

Commit 2345824

Browse files
committed
Compute diff and dirtyDiff on a private editor worker
1 parent 63604de commit 2345824

16 files changed

Lines changed: 805 additions & 126 deletions

File tree

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
'use strict';
6+
7+
import {IWorker, IWorkerFactory} from './workerClient';
8+
import {TPromise, ValueCallback, ErrorCallback} from 'vs/base/common/winjs.base';
9+
import errors = require('vs/base/common/errors');
10+
11+
const INITIALIZE = '$initialize';
12+
13+
interface IMessage {
14+
vsWorker: number;
15+
req?: string;
16+
seq?: string;
17+
}
18+
19+
interface IRequestMessage extends IMessage {
20+
req: string;
21+
method: string;
22+
args: any[];
23+
}
24+
25+
interface IReplyMessage extends IMessage {
26+
seq: string;
27+
err: any;
28+
res: any;
29+
}
30+
31+
interface IMessageReply {
32+
c: ValueCallback;
33+
e: ErrorCallback;
34+
}
35+
36+
interface IMessageHandler {
37+
sendMessage(msg:string): void;
38+
handleMessage(method:string, args:any[]): TPromise<any>;
39+
}
40+
41+
class SimpleWorkerProtocol {
42+
43+
private _workerId: number;
44+
private _lastSentReq: number;
45+
private _pendingReplies: { [req:string]:IMessageReply; };
46+
private _handler:IMessageHandler;
47+
48+
constructor(handler:IMessageHandler) {
49+
this._workerId = -1;
50+
this._handler = handler;
51+
this._lastSentReq = 0;
52+
this._pendingReplies = Object.create(null);
53+
}
54+
55+
public setWorkerId(workerId:number): void {
56+
this._workerId = workerId;
57+
}
58+
59+
public sendMessage(method:string, args:any[]): TPromise<any> {
60+
let req = String(++this._lastSentReq);
61+
let reply: IMessageReply = {
62+
c: null,
63+
e: null
64+
};
65+
let result = new TPromise<any>((c, e, p) => {
66+
reply.c = c;
67+
reply.e = e;
68+
}, () => {
69+
// Cancel not supported
70+
});
71+
this._pendingReplies[req] = reply;
72+
73+
this._send({
74+
vsWorker: this._workerId,
75+
req: req,
76+
method: method,
77+
args: args
78+
});
79+
80+
return result;
81+
}
82+
83+
public handleMessage(serializedMessage:string): void {
84+
let message:IMessage;
85+
try {
86+
message = JSON.parse(serializedMessage);
87+
} catch(e) {
88+
// nothing
89+
}
90+
if (!message.vsWorker) {
91+
return;
92+
}
93+
if (this._workerId !== -1 && message.vsWorker !== this._workerId) {
94+
return;
95+
}
96+
this._handleMessage(message);
97+
}
98+
99+
private _handleMessage(msg:IMessage): void {
100+
if (msg.seq) {
101+
let replyMessage = <IReplyMessage>msg;
102+
if (!this._pendingReplies[replyMessage.seq]) {
103+
console.warn('Got reply to unknown seq');
104+
return;
105+
}
106+
107+
let reply = this._pendingReplies[replyMessage.seq];
108+
delete this._pendingReplies[replyMessage.seq];
109+
110+
if (replyMessage.err) {
111+
let err = replyMessage.err;
112+
if (replyMessage.err.$isError) {
113+
err = new Error();
114+
err.name = replyMessage.err.name;
115+
err.message = replyMessage.err.message;
116+
err.stack = replyMessage.err.stack;
117+
}
118+
reply.e(err);
119+
return;
120+
}
121+
122+
reply.c(replyMessage.res);
123+
return;
124+
}
125+
126+
let requestMessage = <IRequestMessage>msg;
127+
let req = requestMessage.req;
128+
let result = this._handler.handleMessage(requestMessage.method, requestMessage.args);
129+
result.then((r) => {
130+
this._send({
131+
vsWorker: this._workerId,
132+
seq: req,
133+
res: r,
134+
err: undefined
135+
});
136+
}, (e) => {
137+
this._send({
138+
vsWorker: this._workerId,
139+
seq: req,
140+
res: undefined,
141+
err: errors.transformErrorForSerialization(e)
142+
});
143+
});
144+
}
145+
146+
private _send(msg:IRequestMessage|IReplyMessage): void {
147+
let strMsg = JSON.stringify(msg);
148+
// console.log('SENDING: ' + strMsg);
149+
this._handler.sendMessage(strMsg);
150+
}
151+
}
152+
153+
/**
154+
* Main thread side
155+
*/
156+
export class SimpleWorkerClient<T> {
157+
158+
private _worker:IWorker;
159+
private _onModuleLoaded:TPromise<void>;
160+
private _protocol: SimpleWorkerProtocol;
161+
private _proxy: T;
162+
163+
constructor(workerFactory:IWorkerFactory, moduleId:string, ctor:any) {
164+
this._worker = workerFactory.create('vs/base/common/worker/simpleWorker', (msg:string) => {
165+
this._protocol.handleMessage(msg);
166+
});
167+
168+
this._protocol = new SimpleWorkerProtocol({
169+
sendMessage: (msg:string): void => {
170+
this._worker.postMessage(msg);
171+
},
172+
handleMessage: (method:string, args:any[]): TPromise<any> => {
173+
// Intentionally not supporting worker -> main requests
174+
return TPromise.as(null);
175+
}
176+
});
177+
this._protocol.setWorkerId(this._worker.getId());
178+
179+
// Gather loader configuration
180+
let loaderConfiguration:any = null;
181+
let globalRequire = (<any>window).require;
182+
if (typeof globalRequire.getConfig === 'function') {
183+
// Get the configuration from the Monaco AMD Loader
184+
loaderConfiguration = globalRequire.getConfig();
185+
} else if (typeof (<any>window).requirejs !== 'undefined') {
186+
// Get the configuration from requirejs
187+
loaderConfiguration = (<any>window).requirejs.s.contexts._.config;
188+
}
189+
190+
// Send initialize message
191+
this._onModuleLoaded = this._protocol.sendMessage(INITIALIZE, [
192+
this._worker.getId(),
193+
moduleId,
194+
loaderConfiguration
195+
]);
196+
this._onModuleLoaded.then(null, () => this._onError('Worker failed to load ' + moduleId));
197+
198+
// Create proxy to loaded code
199+
let proxyMethodRequest = (method:string, args:any[]):TPromise<any> => {
200+
return this._request(method, args);
201+
};
202+
203+
let createProxyMethod = (method:string, proxyMethodRequest:(method:string, args:any[])=>TPromise<any>): Function => {
204+
return function () {
205+
let args = Array.prototype.slice.call(arguments, 0);
206+
return proxyMethodRequest(method, args);
207+
};
208+
};
209+
210+
this._proxy = <T><any>{};
211+
for (let prop in ctor.prototype) {
212+
if (ctor.prototype.hasOwnProperty(prop)) {
213+
if (typeof ctor.prototype[prop] === 'function') {
214+
this._proxy[prop] = createProxyMethod(prop, proxyMethodRequest);
215+
}
216+
}
217+
}
218+
}
219+
220+
public get(): T {
221+
return this._proxy;
222+
}
223+
224+
private _request(method:string, args:any[]): TPromise<any> {
225+
return this._onModuleLoaded.then(() => {
226+
return this._protocol.sendMessage(method, args);
227+
});
228+
}
229+
230+
private _onError(message:string, error?:any): void {
231+
console.error(message);
232+
console.info(error);
233+
}
234+
}
235+
236+
export interface IRequestHandler {
237+
_requestHandlerTrait: any;
238+
}
239+
240+
/**
241+
* Worker side
242+
*/
243+
export class SimpleWorkerServer {
244+
245+
private _protocol: SimpleWorkerProtocol;
246+
private _requestHandler: IRequestHandler;
247+
248+
constructor(postSerializedMessage:(msg:string)=>void) {
249+
this._protocol = new SimpleWorkerProtocol({
250+
sendMessage: (msg:string): void => {
251+
postSerializedMessage(msg);
252+
},
253+
handleMessage: (method:string, args:any[]): TPromise<any> => this._handleMessage(method, args)
254+
});
255+
}
256+
257+
public onmessage(msg:string): void {
258+
this._protocol.handleMessage(msg);
259+
}
260+
261+
private _handleMessage(method: string, args:any[]): TPromise<any> {
262+
if (method === INITIALIZE) {
263+
return this.initialize(<number>args[0], <string>args[1], <any>args[2]);
264+
}
265+
266+
if (!this._requestHandler || typeof this._requestHandler[method] !== 'function') {
267+
return TPromise.wrapError(new Error('Missing requestHandler or method: ' + method));
268+
}
269+
270+
try {
271+
return TPromise.as(this._requestHandler[method].apply(this._requestHandler, args));
272+
} catch (e) {
273+
return TPromise.wrapError(e);
274+
}
275+
}
276+
277+
private initialize(workerId: number, moduleId: string, loaderConfig:any): TPromise<any> {
278+
this._protocol.setWorkerId(workerId);
279+
280+
// TODO@Alex: share this code with workerServer
281+
if (loaderConfig) {
282+
// Remove 'baseUrl', handling it is beyond scope for now
283+
if (typeof loaderConfig.baseUrl !== 'undefined') {
284+
delete loaderConfig['baseUrl'];
285+
}
286+
if (typeof loaderConfig.paths !== 'undefined') {
287+
if (typeof loaderConfig.paths.vs !== 'undefined') {
288+
delete loaderConfig.paths['vs'];
289+
}
290+
}
291+
let nlsConfig = loaderConfig['vs/nls'];
292+
// We need to have pseudo translation
293+
if (nlsConfig && nlsConfig.pseudo) {
294+
require(['vs/nls'], function(nlsPlugin) {
295+
nlsPlugin.setPseudoTranslation(nlsConfig.pseudo);
296+
});
297+
}
298+
299+
// Since this is in a web worker, enable catching errors
300+
loaderConfig.catchError = true;
301+
(<any>self).require.config(loaderConfig);
302+
}
303+
304+
let cc: ValueCallback;
305+
let ee: ErrorCallback;
306+
let r = new TPromise<any>((c, e, p) => {
307+
cc = c;
308+
ee = e;
309+
});
310+
311+
require([moduleId], (...result:any[]) => {
312+
let handlerModule = result[0];
313+
this._requestHandler = handlerModule.create();
314+
cc(null);
315+
}, ee);
316+
317+
return r;
318+
}
319+
}
320+
321+
/**
322+
* Called on the worker side
323+
*/
324+
export function create(postMessage:(msg:string)=>void): SimpleWorkerServer {
325+
return new SimpleWorkerServer(postMessage);
326+
}

src/vs/base/common/worker/workerClient.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export interface IWorkerCallback {
2222
}
2323

2424
export interface IWorkerFactory {
25-
create(id:number, callback:IWorkerCallback, onCrashCallback?:()=>void):IWorker;
25+
create(moduleId:string, callback:IWorkerCallback, onCrashCallback?:()=>void):IWorker;
2626
}
2727

2828
interface IActiveRequest {
@@ -35,11 +35,8 @@ interface IActiveRequest {
3535

3636
export class WorkerClient {
3737

38-
private static LAST_WORKER_ID = 0;
39-
4038
private _lastMessageId:number;
4139
private _promises:{[id:string]:IActiveRequest;};
42-
private _workerId:number;
4340
private _worker:IWorker;
4441

4542
private _messagesQueue:protocol.IClientMessage[];
@@ -52,7 +49,7 @@ export class WorkerClient {
5249

5350
public onModuleLoaded:TPromise<void>;
5451

55-
constructor(workerFactory:IWorkerFactory, moduleId:string, decodeMessageName:(msg:protocol.IClientMessage)=>string, onCrashCallback:(workerClient:WorkerClient)=>void, workerId:number=++WorkerClient.LAST_WORKER_ID) {
52+
constructor(workerFactory:IWorkerFactory, moduleId:string, decodeMessageName:(msg:protocol.IClientMessage)=>string) {
5653
this._decodeMessageName = decodeMessageName;
5754
this._lastMessageId = 0;
5855
this._promises = {};
@@ -61,11 +58,8 @@ export class WorkerClient {
6158
this._processQueueTimeout = -1;
6259
this._waitingForWorkerReply = false;
6360
this._lastTimerEvent = null;
64-
this._workerId = workerId;
6561

66-
this._worker = workerFactory.create(workerId, (msg) => this._onSerializedMessage(msg), () => {
67-
onCrashCallback(this);
68-
});
62+
this._worker = workerFactory.create('vs/base/common/worker/workerServer', (msg) => this._onSerializedMessage(msg));
6963

7064
let loaderConfiguration:any = null;
7165

@@ -95,10 +89,6 @@ export class WorkerClient {
9589
return this._remoteCom;
9690
}
9791

98-
public get workerId():number {
99-
return this._workerId;
100-
}
101-
10292
public getQueueSize(): number {
10393
return this._messagesQueue.length + (this._waitingForWorkerReply ? 1 : 0);
10494
}

src/vs/base/common/worker/workerServer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ export class WorkerServer {
160160
this._workerId = msg.payload.id;
161161

162162
var loaderConfig = msg.payload.loaderConfiguration;
163+
// TODO@Alex: share this code with simpleWorker
163164
if (loaderConfig) {
164165
// Remove 'baseUrl', handling it is beyond scope for now
165166
if (typeof loaderConfig.baseUrl !== 'undefined') {

0 commit comments

Comments
 (0)