Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/util/vs/base/common/cancellation.ts
13405 views
1
//!!! DO NOT modify, this file was COPIED from 'microsoft/vscode'
2
3
/*---------------------------------------------------------------------------------------------
4
* Copyright (c) Microsoft Corporation. All rights reserved.
5
* Licensed under the MIT License. See License.txt in the project root for license information.
6
*--------------------------------------------------------------------------------------------*/
7
8
import { Emitter, Event } from './event';
9
import { DisposableStore, IDisposable } from './lifecycle';
10
11
export interface CancellationToken {
12
13
/**
14
* A flag signalling is cancellation has been requested.
15
*/
16
readonly isCancellationRequested: boolean;
17
18
/**
19
* An event which fires when cancellation is requested. This event
20
* only ever fires `once` as cancellation can only happen once. Listeners
21
* that are registered after cancellation will be called (next event loop run),
22
* but also only once.
23
*
24
* @event
25
*/
26
readonly onCancellationRequested: (listener: (e: void) => unknown, thisArgs?: unknown, disposables?: IDisposable[]) => IDisposable;
27
}
28
29
const shortcutEvent: Event<void> = Object.freeze(function (callback, context?): IDisposable {
30
const handle = setTimeout(callback.bind(context), 0);
31
return { dispose() { clearTimeout(handle); } };
32
});
33
34
export namespace CancellationToken {
35
36
export function isCancellationToken(thing: unknown): thing is CancellationToken {
37
if (thing === CancellationToken.None || thing === CancellationToken.Cancelled) {
38
return true;
39
}
40
if (thing instanceof MutableToken) {
41
return true;
42
}
43
if (!thing || typeof thing !== 'object') {
44
return false;
45
}
46
return typeof (thing as CancellationToken).isCancellationRequested === 'boolean'
47
&& typeof (thing as CancellationToken).onCancellationRequested === 'function';
48
}
49
50
51
export const None = Object.freeze<CancellationToken>({
52
isCancellationRequested: false,
53
onCancellationRequested: Event.None
54
});
55
56
export const Cancelled = Object.freeze<CancellationToken>({
57
isCancellationRequested: true,
58
onCancellationRequested: shortcutEvent
59
});
60
}
61
62
class MutableToken implements CancellationToken {
63
64
private _isCancelled: boolean = false;
65
private _emitter: Emitter<void> | null = null;
66
67
public cancel() {
68
if (!this._isCancelled) {
69
this._isCancelled = true;
70
if (this._emitter) {
71
this._emitter.fire(undefined);
72
this.dispose();
73
}
74
}
75
}
76
77
get isCancellationRequested(): boolean {
78
return this._isCancelled;
79
}
80
81
get onCancellationRequested(): Event<void> {
82
if (this._isCancelled) {
83
return shortcutEvent;
84
}
85
if (!this._emitter) {
86
this._emitter = new Emitter<void>();
87
}
88
return this._emitter.event;
89
}
90
91
public dispose(): void {
92
if (this._emitter) {
93
this._emitter.dispose();
94
this._emitter = null;
95
}
96
}
97
}
98
99
export class CancellationTokenSource {
100
101
private _token?: CancellationToken = undefined;
102
private _parentListener?: IDisposable = undefined;
103
104
constructor(parent?: CancellationToken) {
105
this._parentListener = parent && parent.onCancellationRequested(this.cancel, this);
106
}
107
108
get token(): CancellationToken {
109
if (!this._token) {
110
// be lazy and create the token only when
111
// actually needed
112
this._token = new MutableToken();
113
}
114
return this._token;
115
}
116
117
cancel(): void {
118
if (!this._token) {
119
// save an object by returning the default
120
// cancelled token when cancellation happens
121
// before someone asks for the token
122
this._token = CancellationToken.Cancelled;
123
124
} else if (this._token instanceof MutableToken) {
125
// actually cancel
126
this._token.cancel();
127
}
128
}
129
130
dispose(cancel: boolean = false): void {
131
if (cancel) {
132
this.cancel();
133
}
134
this._parentListener?.dispose();
135
if (!this._token) {
136
// ensure to initialize with an empty token if we had none
137
this._token = CancellationToken.None;
138
139
} else if (this._token instanceof MutableToken) {
140
// actually dispose
141
this._token.dispose();
142
}
143
}
144
}
145
146
export function cancelOnDispose(store: DisposableStore): CancellationToken {
147
const source = new CancellationTokenSource();
148
store.add({ dispose() { source.cancel(); } });
149
return source.token;
150
}
151
152
/**
153
* A pool that aggregates multiple cancellation tokens. The pool's own token
154
* (accessible via `pool.token`) is cancelled only after every token added
155
* to the pool has been cancelled. Adding tokens after the pool token has
156
* been cancelled has no effect.
157
*/
158
export class CancellationTokenPool {
159
160
private readonly _source = new CancellationTokenSource();
161
private readonly _listeners = new DisposableStore();
162
163
private _total: number = 0;
164
private _cancelled: number = 0;
165
private _isDone: boolean = false;
166
167
get token(): CancellationToken {
168
return this._source.token;
169
}
170
171
/**
172
* Add a token to the pool. If the token is already cancelled it is counted
173
* immediately. Tokens added after the pool token has been cancelled are ignored.
174
*/
175
add(token: CancellationToken): void {
176
if (this._isDone) {
177
return;
178
}
179
180
this._total++;
181
182
if (token.isCancellationRequested) {
183
this._cancelled++;
184
this._check();
185
return;
186
}
187
188
const d = token.onCancellationRequested(() => {
189
d.dispose();
190
this._cancelled++;
191
this._check();
192
});
193
this._listeners.add(d);
194
}
195
196
private _check(): void {
197
if (!this._isDone && this._total > 0 && this._total === this._cancelled) {
198
this._isDone = true;
199
this._listeners.dispose();
200
this._source.cancel();
201
}
202
}
203
204
dispose(): void {
205
this._listeners.dispose();
206
this._source.dispose();
207
}
208
}
209
210