diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index ee44b4bf06f7a313e535f693ea097b3bb8aff1f6..c09d88afe542158400d76f89e655aacc0aa341e6 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -7,6 +7,7 @@ import {IWorker, IWorkerFactory} from './workerClient'; import {TPromise, ValueCallback, ErrorCallback} from 'vs/base/common/winjs.base'; import errors = require('vs/base/common/errors'); +import {Disposable} from 'vs/base/common/lifecycle'; const INITIALIZE = '$initialize'; @@ -153,7 +154,7 @@ class SimpleWorkerProtocol { /** * Main thread side */ -export class SimpleWorkerClient { +export class SimpleWorkerClient extends Disposable { private _worker:IWorker; private _onModuleLoaded:TPromise; @@ -161,9 +162,10 @@ export class SimpleWorkerClient { private _proxy: T; constructor(workerFactory:IWorkerFactory, moduleId:string, ctor:any) { - this._worker = workerFactory.create('vs/base/common/worker/simpleWorker', (msg:string) => { + super(); + this._worker = this._register(workerFactory.create('vs/base/common/worker/simpleWorker', (msg:string) => { this._protocol.handleMessage(msg); - }); + })); this._protocol = new SimpleWorkerProtocol({ sendMessage: (msg:string): void => { diff --git a/src/vs/base/common/worker/workerClient.ts b/src/vs/base/common/worker/workerClient.ts index 839d38589bf6341774549ac7c0ced082a6dfdffb..da033f06c918bcff1adec9358ebb987a9866d9a0 100644 --- a/src/vs/base/common/worker/workerClient.ts +++ b/src/vs/base/common/worker/workerClient.ts @@ -14,7 +14,7 @@ import marshalling = require('vs/base/common/marshalling'); export interface IWorker { getId():number; postMessage(message:string):void; - terminate():void; + dispose():void; } export interface IWorkerCallback { @@ -137,7 +137,7 @@ export class WorkerClient { } } } - this._worker.terminate(); + this._worker.dispose(); } private _sendMessage(type:string, payload:any, forceTimestamp:number=(new Date()).getTime()):TPromise { diff --git a/src/vs/base/worker/defaultWorkerFactory.ts b/src/vs/base/worker/defaultWorkerFactory.ts index c771355c6efed9241099c3e365145511cad7f71c..d3b887aedb1ce06d7eda85ee8511418e8d8406b6 100644 --- a/src/vs/base/worker/defaultWorkerFactory.ts +++ b/src/vs/base/worker/defaultWorkerFactory.ts @@ -22,7 +22,7 @@ var getWorkerUrl = env.getCrossOriginWorkerScriptUrl || defaultGetWorkerUrl; class WebWorker implements IWorker { private id:number; - private worker:any; + private worker:Worker; constructor(moduleId:string, id:number, label:string, onMessageCallback:IWorkerCallback) { this.id = id; @@ -41,8 +41,9 @@ class WebWorker implements IWorker { this.worker.postMessage(msg); } - public terminate(): void { + public dispose(): void { this.worker.terminate(); + this.worker = null; } } @@ -87,6 +88,8 @@ class FrameWorker implements IWorker { public dispose(): void { this._listeners = lifecycle.disposeAll(this._listeners); + window.removeEventListener('message', this.onMessage); + window.frames[this.iframeId()].close(); } private iframeId(): string { @@ -116,11 +119,6 @@ class FrameWorker implements IWorker { this.beforeLoadMessages.push(msg); } } - - public terminate(): void { - window.removeEventListener('message', this.onMessage); - window.frames[this.iframeId()].close(); - } } export class DefaultWorkerFactory implements IWorkerFactory { diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index e1ac87a593f074a9bcdc04d7c116c194da828922..4594b7c1a6c9845aeac0e8777a852480d97b9c2e 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -9,10 +9,21 @@ import URI from 'vs/base/common/uri'; import {TPromise} from 'vs/base/common/winjs.base'; import EditorCommon = require('vs/editor/common/editorCommon'); import {IModelService} from 'vs/editor/common/services/modelService'; -import {IDisposable, disposeAll} from 'vs/base/common/lifecycle'; +import {Disposable, IDisposable, disposeAll} from 'vs/base/common/lifecycle'; import {SimpleWorkerClient} from 'vs/base/common/worker/simpleWorker'; import {DefaultWorkerFactory} from 'vs/base/worker/defaultWorkerFactory'; import {EditorSimpleWorker} from 'vs/editor/common/services/editorSimpleWorkerCommon'; +import {IntervalTimer} from 'vs/base/common/async'; + +/** + * Stop syncing a model to the worker if it was not needed for 1 min. + */ +const STOP_SYNC_MODEL_DELTA_TIME_MS = 60 * 1000; + +/** + * Stop the worker if it was not needed for 5 min. + */ +const STOP_WORKER_DELTA_TIME_MS = 5 * 60 * 1000; export class EditorWorkerServiceImpl implements IEditorWorkerService { public serviceId = IEditorWorkerService; @@ -32,17 +43,43 @@ export class EditorWorkerServiceImpl implements IEditorWorkerService { } } -class WorkerManager { +class WorkerManager extends Disposable { private _modelService:IModelService; private _editorWorkerClient: EditorWorkerClient; + private _lastWorkerUsedTime: number; constructor(modelService:IModelService) { + super(); this._modelService = modelService; this._editorWorkerClient = null; + + let stopWorkerInterval = this._register(new IntervalTimer()); + stopWorkerInterval.cancelAndSet(() => this._checkStopWorker(), Math.round(STOP_WORKER_DELTA_TIME_MS / 2)); + } + + public dispose(): void { + if (this._editorWorkerClient) { + this._editorWorkerClient.dispose(); + this._editorWorkerClient = null; + } + super.dispose(); + } + + private _checkStopWorker(): void { + if (!this._editorWorkerClient) { + return; + } + + let timeSinceLastWorkerUsedTime = (new Date()).getTime() - this._lastWorkerUsedTime; + if (timeSinceLastWorkerUsedTime > STOP_WORKER_DELTA_TIME_MS) { + this._editorWorkerClient.dispose(); + this._editorWorkerClient = null; + } } public withWorker(): TPromise { + this._lastWorkerUsedTime = (new Date()).getTime(); if (!this._editorWorkerClient) { this._editorWorkerClient = new EditorWorkerClient(this._modelService); } @@ -50,26 +87,31 @@ class WorkerManager { } } -class EditorWorkerClient { +class EditorWorkerClient extends Disposable { private _worker: SimpleWorkerClient; private _proxy: EditorSimpleWorker; private _modelService:IModelService; - private _syncedModels: {[uri:string]:IDisposable[];}; + private _syncedModels: {[modelUrl:string]:IDisposable[];}; constructor(modelService:IModelService) { + super(); this._modelService = modelService; this._syncedModels = Object.create(null); - this._worker = new SimpleWorkerClient( + this._worker = this._register(new SimpleWorkerClient( new DefaultWorkerFactory(), 'vs/editor/common/services/editorSimpleWorker', EditorSimpleWorker - ); + )); this._proxy = this._worker.get(); } public dispose(): void { - console.log('TODO: EditorWorkerClient.dispose'); + for (let modelUrl in this._syncedModels) { + disposeAll(this._syncedModels[modelUrl]); + } + this._syncedModels = Object.create(null); + super.dispose(); } public computeDiff(original:URI, modified:URI, ignoreTrimWhitespace:boolean):TPromise {