@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
7
7
import { isNonEmptyArray } from 'vs/base/common/arrays' ;
8
8
import { Barrier } from 'vs/base/common/async' ;
9
9
import { Emitter , Event } from 'vs/base/common/event' ;
10
- import { Disposable } from 'vs/base/common/lifecycle' ;
10
+ import { Disposable , IDisposable , toDisposable } from 'vs/base/common/lifecycle' ;
11
11
import * as perf from 'vs/base/common/performance' ;
12
12
import { isEqualOrParent } from 'vs/base/common/resources' ;
13
13
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService' ;
@@ -68,6 +68,69 @@ export const enum ExtensionRunningPreference {
68
68
Remote
69
69
}
70
70
71
+ class LockCustomer {
72
+ public readonly promise : Promise < IDisposable > ;
73
+ private _resolve ! : ( value : IDisposable ) => void ;
74
+
75
+ constructor (
76
+ public readonly name : string
77
+ ) {
78
+ this . promise = new Promise < IDisposable > ( ( resolve , reject ) => {
79
+ this . _resolve = resolve ;
80
+ } ) ;
81
+ }
82
+
83
+ resolve ( value : IDisposable ) : void {
84
+ this . _resolve ( value ) ;
85
+ }
86
+ }
87
+
88
+ class Lock {
89
+ private readonly _pendingCustomers : LockCustomer [ ] = [ ] ;
90
+ private _isLocked = false ;
91
+
92
+ public async acquire ( customerName : string ) : Promise < IDisposable > {
93
+ const customer = new LockCustomer ( customerName ) ;
94
+ this . _pendingCustomers . push ( customer ) ;
95
+ this . _advance ( ) ;
96
+ return customer . promise ;
97
+ }
98
+
99
+ private _advance ( ) : void {
100
+ if ( this . _isLocked ) {
101
+ // cannot advance yet
102
+ return ;
103
+ }
104
+ if ( this . _pendingCustomers . length === 0 ) {
105
+ // no more waiting customers
106
+ return ;
107
+ }
108
+
109
+ const customer = this . _pendingCustomers . shift ( ) ! ;
110
+
111
+ this . _isLocked = true ;
112
+ let customerHoldsLock = true ;
113
+
114
+ let logLongRunningCustomerTimeout = setTimeout ( ( ) => {
115
+ if ( customerHoldsLock ) {
116
+ console . warn ( `The customer named ${ customer . name } has been holding on to the lock for 30s. This might be a problem.` ) ;
117
+ }
118
+ } , 30 * 1000 /* 30 seconds */ ) ;
119
+
120
+ const releaseLock = ( ) => {
121
+ if ( ! customerHoldsLock ) {
122
+ return ;
123
+ }
124
+ clearTimeout ( logLongRunningCustomerTimeout ) ;
125
+ customerHoldsLock = false ;
126
+ this . _isLocked = false ;
127
+ this . _advance ( ) ;
128
+ } ;
129
+
130
+ customer . resolve ( toDisposable ( releaseLock ) ) ;
131
+ }
132
+ }
133
+
71
134
export abstract class AbstractExtensionService extends Disposable implements IExtensionService {
72
135
73
136
public _serviceBrand : undefined ;
@@ -88,6 +151,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
88
151
public readonly onDidChangeResponsiveChange : Event < IResponsiveStateChangeEvent > = this . _onDidChangeResponsiveChange . event ;
89
152
90
153
protected readonly _registry : ExtensionDescriptionRegistry ;
154
+ private readonly _registryLock : Lock ;
155
+
91
156
private readonly _installedExtensionsReady : Barrier ;
92
157
protected readonly _isDev : boolean ;
93
158
private readonly _extensionsMessages : Map < string , IMessage [ ] > ;
@@ -98,7 +163,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
98
163
99
164
private _deltaExtensionsQueue : DeltaExtensionsQueueItem [ ] ;
100
165
private _inHandleDeltaExtensions : boolean ;
101
- private readonly _onDidFinishHandleDeltaExtensions = this . _register ( new Emitter < void > ( ) ) ;
102
166
103
167
protected _runningLocation : Map < string , ExtensionRunningLocation > ;
104
168
@@ -130,6 +194,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
130
194
} ) ) ;
131
195
132
196
this . _registry = new ExtensionDescriptionRegistry ( [ ] ) ;
197
+ this . _registryLock = new Lock ( ) ;
198
+
133
199
this . _installedExtensionsReady = new Barrier ( ) ;
134
200
this . _isDev = ! this . _environmentService . isBuilt || this . _environmentService . isExtensionDevelopment ;
135
201
this . _extensionsMessages = new Map < string , IMessage [ ] > ( ) ;
@@ -207,17 +273,20 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
207
273
return ;
208
274
}
209
275
210
- while ( this . _deltaExtensionsQueue . length > 0 ) {
211
- const item = this . _deltaExtensionsQueue . shift ( ) ! ;
212
- try {
213
- this . _inHandleDeltaExtensions = true ;
276
+ let lock : IDisposable | null = null ;
277
+ try {
278
+ this . _inHandleDeltaExtensions = true ;
279
+ lock = await this . _registryLock . acquire ( 'handleDeltaExtensions' ) ;
280
+ while ( this . _deltaExtensionsQueue . length > 0 ) {
281
+ const item = this . _deltaExtensionsQueue . shift ( ) ! ;
214
282
await this . _deltaExtensions ( item . toAdd , item . toRemove ) ;
215
- } finally {
216
- this . _inHandleDeltaExtensions = false ;
283
+ }
284
+ } finally {
285
+ this . _inHandleDeltaExtensions = false ;
286
+ if ( lock ) {
287
+ lock . dispose ( ) ;
217
288
}
218
289
}
219
-
220
- this . _onDidFinishHandleDeltaExtensions . fire ( ) ;
221
290
}
222
291
223
292
private async _deltaExtensions ( _toAdd : IExtension [ ] , _toRemove : string [ ] | IExtension [ ] ) : Promise < void > {
@@ -566,11 +635,17 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
566
635
public async startExtensionHosts ( ) : Promise < void > {
567
636
this . stopExtensionHosts ( ) ;
568
637
569
- if ( this . _inHandleDeltaExtensions ) {
570
- await Event . toPromise ( this . _onDidFinishHandleDeltaExtensions . event ) ;
571
- }
638
+ const lock = await this . _registryLock . acquire ( 'startExtensionHosts' ) ;
639
+ try {
640
+ this . _startExtensionHosts ( false , Array . from ( this . _allRequestedActivateEvents . keys ( ) ) ) ;
572
641
573
- this . _startExtensionHosts ( false , Array . from ( this . _allRequestedActivateEvents . keys ( ) ) ) ;
642
+ const localProcessExtensionHost = this . _getExtensionHostManager ( ExtensionHostKind . LocalProcess ) ;
643
+ if ( localProcessExtensionHost ) {
644
+ await localProcessExtensionHost . ready ( ) ;
645
+ }
646
+ } finally {
647
+ lock . dispose ( ) ;
648
+ }
574
649
}
575
650
576
651
public async restartExtensionHost ( ) : Promise < void > {
0 commit comments