@@ -111,17 +111,18 @@ public actor BuildSystemManager {
111
111
/// Create a BuildSystemManager that wraps the given build system. The new
112
112
/// manager will modify the delegate of the underlying build system.
113
113
public init ( buildSystem: BuildSystem ? , fallbackBuildSystem: FallbackBuildSystem ? ,
114
- mainFilesProvider: MainFilesProvider ? , fallbackSettingsTimeout: DispatchTimeInterval = . seconds( 3 ) ) {
115
- precondition ( buildSystem? . delegate == nil )
114
+ mainFilesProvider: MainFilesProvider ? , fallbackSettingsTimeout: DispatchTimeInterval = . seconds( 3 ) ) async {
115
+ let buildSystemHasDelegate = await buildSystem? . delegate != nil
116
+ precondition ( !buildSystemHasDelegate)
116
117
self . buildSystem = buildSystem
117
118
self . fallbackBuildSystem = fallbackBuildSystem
118
119
self . _mainFilesProvider = mainFilesProvider
119
120
self . fallbackSettingsTimeout = fallbackSettingsTimeout
120
- self . buildSystem? . delegate = self
121
+ await self . buildSystem? . setDelegate ( self )
121
122
}
122
123
123
- public func filesDidChange( _ events: [ FileEvent ] ) {
124
- self . buildSystem? . filesDidChange ( events)
124
+ public func filesDidChange( _ events: [ FileEvent ] ) async {
125
+ await self . buildSystem? . filesDidChange ( events)
125
126
self . fallbackBuildSystem? . filesDidChange ( events)
126
127
}
127
128
}
@@ -182,7 +183,7 @@ extension BuildSystemManager {
182
183
}
183
184
}
184
185
185
- public func registerForChangeNotifications( for uri: DocumentURI , language: Language ) {
186
+ public func registerForChangeNotifications( for uri: DocumentURI , language: Language ) async {
186
187
log ( " registerForChangeNotifications( \( uri. pseudoPath) ) " )
187
188
let mainFile : DocumentURI
188
189
@@ -194,7 +195,7 @@ extension BuildSystemManager {
194
195
self . watchedFiles [ uri] = ( mainFile, language)
195
196
}
196
197
197
- let newStatus = self . cachedStatusOrRegisterForSettings ( for: mainFile, language: language)
198
+ let newStatus = await self . cachedStatusOrRegisterForSettings ( for: mainFile, language: language)
198
199
199
200
if let mainChange = newStatus. buildSettingsChange,
200
201
let delegate = self . _delegate {
@@ -206,7 +207,7 @@ extension BuildSystemManager {
206
207
/// Return settings for `file` based on the `change` settings for `mainFile`.
207
208
///
208
209
/// This is used when inferring arguments for header files (e.g. main file is a `.m` while file is a` .h`).
209
- func convert(
210
+ nonisolated func convert(
210
211
change: FileBuildSettingsChange ,
211
212
ofMainFile mainFile: DocumentURI ,
212
213
to file: DocumentURI
@@ -227,7 +228,7 @@ extension BuildSystemManager {
227
228
func cachedStatusOrRegisterForSettings(
228
229
for mainFile: DocumentURI ,
229
230
language: Language
230
- ) -> MainFileStatus {
231
+ ) async -> MainFileStatus {
231
232
// If we already have a status for the main file, use that.
232
233
// Don't update any existing timeout.
233
234
if let status = self . mainFileStatuses [ mainFile] {
@@ -248,7 +249,7 @@ extension BuildSystemManager {
248
249
// Intentionally register with the `BuildSystem` after setting the fallback to allow for
249
250
// testing of the fallback system triggering before the `BuildSystem` can reply (e.g. if a
250
251
// fallback time of 0 is specified).
251
- buildSystem. registerForChangeNotifications ( for: mainFile, language: language)
252
+ await buildSystem. registerForChangeNotifications ( for: mainFile, language: language)
252
253
253
254
254
255
newStatus = . waiting
@@ -263,6 +264,16 @@ extension BuildSystemManager {
263
264
} else { // Don't have any build systems.
264
265
newStatus = . unsupported
265
266
}
267
+
268
+ if let status = self . mainFileStatuses [ mainFile] {
269
+ // Since we await above, another call to `cachedStatusOrRegisterForSettings`
270
+ // might have set the main file status of `mainFile`. If this race happened,
271
+ // return the value set by the concurrently executing function. This is safe
272
+ // since all calls from this function are either side-effect free or
273
+ // idempotent.
274
+ return status
275
+ }
276
+
266
277
self . mainFileStatuses [ mainFile] = newStatus
267
278
return newStatus
268
279
}
@@ -321,27 +332,30 @@ extension BuildSystemManager {
321
332
}
322
333
}
323
334
324
- public func unregisterForChangeNotifications( for uri: DocumentURI ) {
335
+ public func unregisterForChangeNotifications( for uri: DocumentURI ) async {
325
336
guard let mainFile = self . watchedFiles [ uri] ? . mainFile else {
326
337
log ( " Unbalanced calls for registerForChangeNotifications and unregisterForChangeNotifications " , level: . warning)
327
338
return
328
339
}
329
340
self . watchedFiles [ uri] = nil
330
- self . checkUnreferencedMainFile ( mainFile)
341
+ await self . checkUnreferencedMainFile ( mainFile)
331
342
}
332
343
333
344
/// If the given main file is no longer referenced by any watched files,
334
345
/// remove it and unregister it at the underlying build system.
335
- func checkUnreferencedMainFile( _ mainFile: DocumentURI ) {
346
+ func checkUnreferencedMainFile( _ mainFile: DocumentURI ) async {
336
347
if !self . watchedFiles. values. lazy. map ( { $0. mainFile } ) . contains ( mainFile) {
337
348
// This was the last reference to the main file. Remove it.
338
- self . buildSystem? . unregisterForChangeNotifications ( for: mainFile)
349
+ await self . buildSystem? . unregisterForChangeNotifications ( for: mainFile)
339
350
self . mainFileStatuses [ mainFile] = nil
340
351
}
341
352
}
342
353
343
- public nonisolated func fileHandlingCapability( for uri: DocumentURI ) -> FileHandlingCapability {
344
- return max ( buildSystem? . fileHandlingCapability ( for: uri) ?? . unhandled, fallbackBuildSystem? . fileHandlingCapability ( for: uri) ?? . unhandled)
354
+ public func fileHandlingCapability( for uri: DocumentURI ) async -> FileHandlingCapability {
355
+ return max (
356
+ await buildSystem? . fileHandlingCapability ( for: uri) ?? . unhandled,
357
+ fallbackBuildSystem? . fileHandlingCapability ( for: uri) ?? . unhandled
358
+ )
345
359
}
346
360
}
347
361
@@ -444,7 +458,7 @@ extension BuildSystemManager: MainFilesDelegate {
444
458
}
445
459
446
460
// FIXME: Consider debouncing/limiting this, seems to trigger often during a build.
447
- public func mainFilesChangedImpl( ) {
461
+ public func mainFilesChangedImpl( ) async {
448
462
let origWatched = self . watchedFiles
449
463
self . watchedFiles = [ : ]
450
464
var buildSettingsChanges = [ DocumentURI: FileBuildSettingsChange] ( )
@@ -458,9 +472,9 @@ extension BuildSystemManager: MainFilesDelegate {
458
472
459
473
if state. mainFile != newMainFile {
460
474
log ( " main file for ' \( uri) ' changed old: ' \( state. mainFile) ' -> new: ' \( newMainFile) ' " , level: . info)
461
- self . checkUnreferencedMainFile ( state. mainFile)
475
+ await self . checkUnreferencedMainFile ( state. mainFile)
462
476
463
- let newStatus = self . cachedStatusOrRegisterForSettings (
477
+ let newStatus = await self . cachedStatusOrRegisterForSettings (
464
478
for: newMainFile, language: language)
465
479
if let change = newStatus. buildSettingsChange {
466
480
let newChange = self . convert ( change: change, ofMainFile: newMainFile, to: uri)
0 commit comments