From fc7e9312d00e60171ed77c477ed91231d3dbfff9 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sun, 12 Dec 2021 17:48:47 +0100 Subject: build: move modules into subproject directory --- .../src/main/js/utils/ConditionVariable.ts | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 subprojects/language-web/src/main/js/utils/ConditionVariable.ts (limited to 'subprojects/language-web/src/main/js/utils/ConditionVariable.ts') diff --git a/subprojects/language-web/src/main/js/utils/ConditionVariable.ts b/subprojects/language-web/src/main/js/utils/ConditionVariable.ts new file mode 100644 index 00000000..0910dfa6 --- /dev/null +++ b/subprojects/language-web/src/main/js/utils/ConditionVariable.ts @@ -0,0 +1,64 @@ +import { getLogger } from './logger'; +import { PendingTask } from './PendingTask'; + +const log = getLogger('utils.ConditionVariable'); + +export type Condition = () => boolean; + +export class ConditionVariable { + condition: Condition; + + defaultTimeout: number; + + listeners: PendingTask[] = []; + + constructor(condition: Condition, defaultTimeout = 0) { + this.condition = condition; + this.defaultTimeout = defaultTimeout; + } + + async waitFor(timeoutMs: number | null = null): Promise { + if (this.condition()) { + return; + } + const timeoutOrDefault = timeoutMs || this.defaultTimeout; + let nowMs = Date.now(); + const endMs = nowMs + timeoutOrDefault; + while (!this.condition() && nowMs < endMs) { + const remainingMs = endMs - nowMs; + const promise = new Promise((resolve, reject) => { + if (this.condition()) { + resolve(); + return; + } + const task = new PendingTask(resolve, reject, remainingMs); + this.listeners.push(task); + }); + // We must keep waiting until the update has completed, + // so the tasks can't be started in parallel. + // eslint-disable-next-line no-await-in-loop + await promise; + nowMs = Date.now(); + } + if (!this.condition()) { + log.error('Condition still does not hold after', timeoutOrDefault, 'ms'); + throw new Error('Failed to wait for condition'); + } + } + + notifyAll(): void { + this.clearListenersWith((listener) => listener.resolve()); + } + + rejectAll(error: unknown): void { + this.clearListenersWith((listener) => listener.reject(error)); + } + + private clearListenersWith(callback: (listener: PendingTask) => void) { + // Copy `listeners` so that we don't get into a race condition + // if one of the listeners adds another listener. + const { listeners } = this; + this.listeners = []; + listeners.forEach(callback); + } +} -- cgit v1.2.3-70-g09d2