From 831c6a2d45a6ba04d404f555458c402ec668bc37 Mon Sep 17 00:00:00 2001 From: Glenn Sarti Date: Tue, 4 May 2021 15:24:36 +0800 Subject: [PATCH] (GH-618) Only trigger auto updates once Previously the extension would trigger a new auto-update whenever it was activated. However this can causes issues when a user deactivates-activates the language server quickly multiple times, as it cause multiple timeouts to be created. Additionally, when disabled, it would not clear any active timeouts. This commit adds a small wrapper around the setTimeout function to track usage. In particular, it will silently ignore calls to add a timeout if a timeout is already active. This commit also clears any timeouts when the language server is disabled. --- src/extension.ts | 9 +++++++-- src/utils.ts | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 14f08e1ffa..f20365a67e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -23,7 +23,10 @@ import { prunedFolderNames, sortedWorkspaceFolders } from './vscodeUtils'; -import { sleep } from './utils'; +import { + SingleInstanceTimeout, + sleep +} from './utils'; interface terraformLanguageClient { commandPrefix: string, @@ -40,6 +43,7 @@ const appInsightsKey = '885372d2-6f3c-499f-9d25-b8b219983a52'; let reporter: TelemetryReporter; let installPath: string; +let languageServerUpdater = new SingleInstanceTimeout(); export async function activate(context: vscode.ExtensionContext): Promise { const extensionVersion = vscode.extensions.getExtension(extensionId).packageJSON.version; @@ -70,6 +74,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { const current = config('terraform').get('languageServer'); await config('terraform').update('languageServer', Object.assign(current, { external: false }), vscode.ConfigurationTarget.Global); } + languageServerUpdater.clear(); return stopClients(); }), vscode.commands.registerCommand('terraform.apply', async () => { @@ -168,7 +173,7 @@ export function deactivate(): Promise { async function updateLanguageServer() { const delay = 1000 * 60 * 60 * 24; - setTimeout(updateLanguageServer, delay); // check for new updates every 24hrs + languageServerUpdater.timeout(updateLanguageServer, delay); // check for new updates every 24hrs // skip install if a language server binary path is set if (!config('terraform').get('languageServer.pathToBinary')) { diff --git a/src/utils.ts b/src/utils.ts index cf7f066c62..23d854b399 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -34,3 +34,22 @@ export function httpsRequest(url: string, options: https.RequestOptions = {}, en export async function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } + +// A small wrapper around setTimeout which ensures that only a single timeout +// timer can be running at a time. Attempts to add a new timeout silently fail. +export class SingleInstanceTimeout { + private timerLock = false; + private timerId: NodeJS.Timeout; + + public timeout(fn, delay, ...args) { + if (!this.timerLock) { + this.timerLock = true; + this.timerId = setTimeout(function () { this.timerLock = false; fn() }, delay, args) + } + } + + public clear() { + if (this.timerId) { clearTimeout(this.timerId) } + this.timerLock = false; + } +}