Path: blob/main/extensions/copilot/src/util/vs/base/common/cancellation.ts
13405 views
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'12/*---------------------------------------------------------------------------------------------3* Copyright (c) Microsoft Corporation. All rights reserved.4* Licensed under the MIT License. See License.txt in the project root for license information.5*--------------------------------------------------------------------------------------------*/67import { Emitter, Event } from './event';8import { DisposableStore, IDisposable } from './lifecycle';910export interface CancellationToken {1112/**13* A flag signalling is cancellation has been requested.14*/15readonly isCancellationRequested: boolean;1617/**18* An event which fires when cancellation is requested. This event19* only ever fires `once` as cancellation can only happen once. Listeners20* that are registered after cancellation will be called (next event loop run),21* but also only once.22*23* @event24*/25readonly onCancellationRequested: (listener: (e: void) => unknown, thisArgs?: unknown, disposables?: IDisposable[]) => IDisposable;26}2728const shortcutEvent: Event<void> = Object.freeze(function (callback, context?): IDisposable {29const handle = setTimeout(callback.bind(context), 0);30return { dispose() { clearTimeout(handle); } };31});3233export namespace CancellationToken {3435export function isCancellationToken(thing: unknown): thing is CancellationToken {36if (thing === CancellationToken.None || thing === CancellationToken.Cancelled) {37return true;38}39if (thing instanceof MutableToken) {40return true;41}42if (!thing || typeof thing !== 'object') {43return false;44}45return typeof (thing as CancellationToken).isCancellationRequested === 'boolean'46&& typeof (thing as CancellationToken).onCancellationRequested === 'function';47}484950export const None = Object.freeze<CancellationToken>({51isCancellationRequested: false,52onCancellationRequested: Event.None53});5455export const Cancelled = Object.freeze<CancellationToken>({56isCancellationRequested: true,57onCancellationRequested: shortcutEvent58});59}6061class MutableToken implements CancellationToken {6263private _isCancelled: boolean = false;64private _emitter: Emitter<void> | null = null;6566public cancel() {67if (!this._isCancelled) {68this._isCancelled = true;69if (this._emitter) {70this._emitter.fire(undefined);71this.dispose();72}73}74}7576get isCancellationRequested(): boolean {77return this._isCancelled;78}7980get onCancellationRequested(): Event<void> {81if (this._isCancelled) {82return shortcutEvent;83}84if (!this._emitter) {85this._emitter = new Emitter<void>();86}87return this._emitter.event;88}8990public dispose(): void {91if (this._emitter) {92this._emitter.dispose();93this._emitter = null;94}95}96}9798export class CancellationTokenSource {99100private _token?: CancellationToken = undefined;101private _parentListener?: IDisposable = undefined;102103constructor(parent?: CancellationToken) {104this._parentListener = parent && parent.onCancellationRequested(this.cancel, this);105}106107get token(): CancellationToken {108if (!this._token) {109// be lazy and create the token only when110// actually needed111this._token = new MutableToken();112}113return this._token;114}115116cancel(): void {117if (!this._token) {118// save an object by returning the default119// cancelled token when cancellation happens120// before someone asks for the token121this._token = CancellationToken.Cancelled;122123} else if (this._token instanceof MutableToken) {124// actually cancel125this._token.cancel();126}127}128129dispose(cancel: boolean = false): void {130if (cancel) {131this.cancel();132}133this._parentListener?.dispose();134if (!this._token) {135// ensure to initialize with an empty token if we had none136this._token = CancellationToken.None;137138} else if (this._token instanceof MutableToken) {139// actually dispose140this._token.dispose();141}142}143}144145export function cancelOnDispose(store: DisposableStore): CancellationToken {146const source = new CancellationTokenSource();147store.add({ dispose() { source.cancel(); } });148return source.token;149}150151/**152* A pool that aggregates multiple cancellation tokens. The pool's own token153* (accessible via `pool.token`) is cancelled only after every token added154* to the pool has been cancelled. Adding tokens after the pool token has155* been cancelled has no effect.156*/157export class CancellationTokenPool {158159private readonly _source = new CancellationTokenSource();160private readonly _listeners = new DisposableStore();161162private _total: number = 0;163private _cancelled: number = 0;164private _isDone: boolean = false;165166get token(): CancellationToken {167return this._source.token;168}169170/**171* Add a token to the pool. If the token is already cancelled it is counted172* immediately. Tokens added after the pool token has been cancelled are ignored.173*/174add(token: CancellationToken): void {175if (this._isDone) {176return;177}178179this._total++;180181if (token.isCancellationRequested) {182this._cancelled++;183this._check();184return;185}186187const d = token.onCancellationRequested(() => {188d.dispose();189this._cancelled++;190this._check();191});192this._listeners.add(d);193}194195private _check(): void {196if (!this._isDone && this._total > 0 && this._total === this._cancelled) {197this._isDone = true;198this._listeners.dispose();199this._source.cancel();200}201}202203dispose(): void {204this._listeners.dispose();205this._source.dispose();206}207}208209210