@@ -71,7 +71,7 @@ export class CachingLocator implements ILocator {
71
71
this . opts = normalizeCachingLocatorOptions ( opts ) ;
72
72
// eslint-disable-next-line @typescript-eslint/no-use-before-define
73
73
this . looper = new BackgroundLooper ( {
74
- runDefault : ( ) => this . doRefresh ( ) ,
74
+ runDefault : ( ) => this . refresh ( ) ,
75
75
retry : {
76
76
intervalms : this . opts . refreshRetryMinutes * 60 * 1000 ,
77
77
} ,
@@ -96,8 +96,20 @@ export class CachingLocator implements ILocator {
96
96
97
97
await this . cache . initialize ( ) ;
98
98
this . looper . start ( ) ;
99
- await this . initialRefresh ( ) ;
100
- this . locator . onChanged ( ( event ) => this . handleChange ( event ) ) ;
99
+
100
+ this . locator . onChanged ( ( event ) => this . ensureCurrentRefresh ( event ) ) ;
101
+
102
+ // Do the initial refresh.
103
+ const envs = this . cache . getAllEnvs ( ) ;
104
+ if ( envs !== undefined ) {
105
+ this . initializing . resolve ( ) ;
106
+ await this . ensureRecentRefresh ( ) ;
107
+ } else {
108
+ // There is nothing in the cache, so we must wait for the
109
+ // initial refresh to finish before allowing iteration.
110
+ await this . ensureRecentRefresh ( ) ;
111
+ this . initializing . resolve ( ) ;
112
+ }
101
113
}
102
114
103
115
public dispose ( ) : void {
@@ -134,7 +146,7 @@ export class CachingLocator implements ILocator {
134
146
const envs = this . cache . getAllEnvs ( ) ;
135
147
if ( envs !== undefined ) {
136
148
envs . push ( resolved ) ;
137
- await this . update ( envs ) ;
149
+ await this . updateCache ( envs ) ;
138
150
}
139
151
}
140
152
return resolved ;
@@ -183,40 +195,62 @@ export class CachingLocator implements ILocator {
183
195
}
184
196
185
197
/**
186
- * Trigger a refresh of the cache from the downstream locator.
198
+ * Maybe trigger a refresh of the cache from the downstream locator.
187
199
*
188
- * Note that if a refresh has already been requested or is currently
189
- * running, this is a noop.
200
+ * If a refresh isn't already running then we request a refresh and
201
+ * wait for it to finish. Otherwise we do not make a new request,
202
+ * but instead only wait for the last requested refresh to complete.
190
203
*/
191
- private refresh ( ) : Promise < void > {
204
+ private ensureRecentRefresh ( ) : Promise < void > {
192
205
// Re-use the last req in the queue if possible.
193
206
const last = this . looper . getLastRequest ( ) ;
194
207
if ( last !== undefined ) {
195
208
const [ , promise ] = last ;
196
209
return promise ;
197
210
}
198
211
// The queue is empty so add a new request.
199
- const [ , waitUntilDone ] = this . looper . addRequest ( ) ;
212
+ const [ , waitUntilDone ] = this . looper . addRequest ( ( ) => this . refresh ( ) ) ;
200
213
return waitUntilDone ;
201
214
}
202
215
216
+ /**
217
+ * Maybe trigger a refresh of the cache from the downstream locator.
218
+ *
219
+ * Make sure that a completely new refresh will be started soon and
220
+ * wait for it to finish. If a refresh isn't already running then
221
+ * we start one and wait for it to finish. If one is already
222
+ * running then we make sure a new one is requested to start after
223
+ * that and wait for it to finish. That means if one is already
224
+ * waiting in the queue then we wait for that one instead of making
225
+ * a new request.
226
+ */
227
+ private ensureCurrentRefresh ( event ?: PythonEnvsChangedEvent ) : void {
228
+ const req = this . looper . getNextRequest ( ) ;
229
+ if ( req === undefined ) {
230
+ // There isn't already a pending request (due to an
231
+ // onChanged event), so we add one.
232
+ this . looper . addRequest ( ( ) => this . refresh ( event ) ) ;
233
+ }
234
+ // Otherwise let the pending request take care of it.
235
+ }
236
+
203
237
/**
204
238
* Immediately perform a refresh of the cache from the downstream locator.
205
239
*
206
- * It does not matter if another refresh is already
240
+ * It does not matter if another refresh is already running.
207
241
*/
208
- private async doRefresh (
242
+ private async refresh (
209
243
event ?: PythonEnvsChangedEvent ,
210
244
) : Promise < void > {
211
245
const iterator = this . locator . iterEnvs ( ) ;
212
246
const envs = await getEnvs ( iterator ) ;
213
- await this . update ( envs , event ) ;
247
+ await this . updateCache ( envs , event ) ;
214
248
}
215
249
216
250
/**
217
251
* Set the cache to the given envs, flush, and emit an onChanged event.
218
252
*/
219
- private async update (
253
+ private async updateCache (
220
254
envs : PythonEnvInfo [ ] ,
221
255
event ?: PythonEnvsChangedEvent ,
222
256
) : Promise < void > {
@@ -225,29 +259,6 @@ export class CachingLocator implements ILocator {
225
259
await this . cache . flush ( ) ;
226
260
this . watcher . fire ( event || { } ) ; // Emit an "onCHanged" event.
227
261
}
228
-
229
- private handleChange ( event : PythonEnvsChangedEvent ) : void {
230
- const req = this . looper . getNextRequest ( ) ;
231
- if ( req === undefined ) {
232
- // There isn't already a pending request (due to an
233
- // onChanged event), so we add one.
234
- this . looper . addRequest ( ( ) => this . doRefresh ( event ) ) ;
235
- }
236
- // Otherwise let the pending request take care of it.
237
- }
238
-
239
- private async initialRefresh ( ) : Promise < void > {
240
- const envs = this . cache . getAllEnvs ( ) ;
241
- if ( envs !== undefined ) {
242
- this . initializing . resolve ( ) ;
243
- await this . refresh ( ) ;
244
- } else {
245
- // There is nothing in the cache, so we must wait for the
246
- // initial refresh to finish before allowing iteration.
247
- await this . refresh ( ) ;
248
- this . initializing . resolve ( ) ;
249
- }
250
- }
251
262
}
252
263
253
264
type RequestID = number ;
0 commit comments