Skip to content

Commit 32af558

Browse files
committed
Migrate BuildServerBuildSystem to an actor and make methods in BuildSystemDelegate async
This concludes the migration of the build systems to async.
1 parent 5a32f95 commit 32af558

10 files changed

+91
-101
lines changed

Sources/SKCore/BuildServerBuildSystem.swift

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,7 @@ func executable(_ name: String) -> String {
4343
///
4444
/// Provides build settings from a build server launched based on a
4545
/// `buildServer.json` configuration file provided in the repo root.
46-
public final class BuildServerBuildSystem: MessageHandler {
47-
/// The handler's request queue. `delegate` will always be called on this queue.
48-
public let queue: DispatchQueue = DispatchQueue(label: "language-server-queue", qos: .userInitiated)
49-
46+
public actor BuildServerBuildSystem: MessageHandler {
5047
let projectRoot: AbsolutePath
5148
let buildFolder: AbsolutePath?
5249
let serverConfig: BuildServerConfig
@@ -73,7 +70,7 @@ public final class BuildServerBuildSystem: MessageHandler {
7370
/// The build settings that have been received from the build server.
7471
private var buildSettings: [DocumentURI: FileBuildSettings] = [:]
7572

76-
public init(projectRoot: AbsolutePath, buildFolder: AbsolutePath?, fileSystem: FileSystem = localFileSystem) throws {
73+
public init(projectRoot: AbsolutePath, buildFolder: AbsolutePath?, fileSystem: FileSystem = localFileSystem) async throws {
7774
let configPath = projectRoot.appending(component: "buildServer.json")
7875
let config = try loadBuildServerConfig(path: configPath, fileSystem: fileSystem)
7976
#if os(Windows)
@@ -95,12 +92,11 @@ public final class BuildServerBuildSystem: MessageHandler {
9592
/// Creates a build system using the Build Server Protocol config.
9693
///
9794
/// - Returns: nil if `projectRoot` has no config or there is an error parsing it.
98-
public convenience init?(projectRoot: AbsolutePath?, buildSetup: BuildSetup)
99-
{
95+
public init?(projectRoot: AbsolutePath?, buildSetup: BuildSetup) async {
10096
if projectRoot == nil { return nil }
10197

10298
do {
103-
try self.init(projectRoot: projectRoot!, buildFolder: buildSetup.path)
99+
try await self.init(projectRoot: projectRoot!, buildFolder: buildSetup.path)
104100
} catch _ as FileSystemError {
105101
// config file was missing, no build server for this workspace
106102
return nil
@@ -171,13 +167,11 @@ public final class BuildServerBuildSystem: MessageHandler {
171167
/// the build server has sent us a notification.
172168
///
173169
/// We need to notify the delegate about any updated build settings.
174-
public func handle(_ params: some NotificationType, from clientID: ObjectIdentifier) {
175-
queue.async {
176-
if let params = params as? BuildTargetsChangedNotification {
177-
self.handleBuildTargetsChanged(Notification(params, clientID: clientID))
178-
} else if let params = params as? FileOptionsChangedNotification {
179-
self.handleFileOptionsChanged(Notification(params, clientID: clientID))
180-
}
170+
public func handle(_ params: some NotificationType, from clientID: ObjectIdentifier) async {
171+
if let params = params as? BuildTargetsChangedNotification {
172+
await self.handleBuildTargetsChanged(Notification(params, clientID: clientID))
173+
} else if let params = params as? FileOptionsChangedNotification {
174+
await self.handleFileOptionsChanged(Notification(params, clientID: clientID))
181175
}
182176
}
183177

@@ -190,30 +184,28 @@ public final class BuildServerBuildSystem: MessageHandler {
190184
from clientID: ObjectIdentifier,
191185
reply: @escaping (LSPResult<R.Response>) -> Void
192186
) {
193-
queue.async {
194-
reply(.failure(ResponseError.methodNotFound(R.method)))
195-
}
187+
reply(.failure(ResponseError.methodNotFound(R.method)))
196188
}
197189

198-
func handleBuildTargetsChanged(_ notification: LanguageServerProtocol.Notification<BuildTargetsChangedNotification>) {
199-
self.delegate?.buildTargetsChanged(notification.params.changes)
190+
func handleBuildTargetsChanged(_ notification: LanguageServerProtocol.Notification<BuildTargetsChangedNotification>) async {
191+
await self.delegate?.buildTargetsChanged(notification.params.changes)
200192
}
201193

202-
func handleFileOptionsChanged(_ notification: LanguageServerProtocol.Notification<FileOptionsChangedNotification>) {
194+
func handleFileOptionsChanged(_ notification: LanguageServerProtocol.Notification<FileOptionsChangedNotification>) async {
203195
let result = notification.params.updatedOptions
204196
let settings = FileBuildSettings(
205197
compilerArguments: result.options, workingDirectory: result.workingDirectory)
206-
self.buildSettingsChanged(for: notification.params.uri, settings: settings)
198+
await self.buildSettingsChanged(for: notification.params.uri, settings: settings)
207199
}
208200

209201
/// Record the new build settings for the given document and inform the delegate
210202
/// about the changed build settings.
211-
private func buildSettingsChanged(for document: DocumentURI, settings: FileBuildSettings?) {
203+
private func buildSettingsChanged(for document: DocumentURI, settings: FileBuildSettings?) async {
212204
buildSettings[document] = settings
213205
if let settings {
214-
self.delegate?.fileBuildSettingsChanged([document: .modified(settings)])
206+
await self.delegate?.fileBuildSettingsChanged([document: .modified(settings)])
215207
} else {
216-
self.delegate?.fileBuildSettingsChanged([document: .removedOrUnavailable])
208+
await self.delegate?.fileBuildSettingsChanged([document: .removedOrUnavailable])
217209
}
218210
}
219211
}
@@ -239,12 +231,14 @@ extension BuildServerBuildSystem: BuildSystem {
239231
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
240232
let request = RegisterForChanges(uri: uri, action: .register)
241233
_ = self.buildServer?.send(request, queue: requestQueue, reply: { result in
242-
if let error = result.failure {
243-
log("error registering \(uri): \(error)", level: .error)
244-
245-
// BuildServer registration failed, so tell our delegate that no build
246-
// settings are available.
247-
self.buildSettingsChanged(for: uri, settings: nil)
234+
Task {
235+
if let error = result.failure {
236+
log("error registering \(uri): \(error)", level: .error)
237+
238+
// BuildServer registration failed, so tell our delegate that no build
239+
// settings are available.
240+
await self.buildSettingsChanged(for: uri, settings: nil)
241+
}
248242
}
249243
})
250244
}

Sources/SKCore/BuildSystemDelegate.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,25 @@ public protocol BuildSystemDelegate: AnyObject {
1818
///
1919
/// The callee should request new sources and outputs for the build targets of
2020
/// interest.
21-
func buildTargetsChanged(_ changes: [BuildTargetEvent])
21+
func buildTargetsChanged(_ changes: [BuildTargetEvent]) async
2222

2323
/// Notify the delegate that the given files' build settings have changed.
2424
///
2525
/// The delegate should cache the new build settings for any of the given
2626
/// files that they are interested in.
2727
func fileBuildSettingsChanged(
28-
_ changedFiles: [DocumentURI: FileBuildSettingsChange])
28+
_ changedFiles: [DocumentURI: FileBuildSettingsChange]) async
2929

3030
/// Notify the delegate that the dependencies of the given files have changed
3131
/// and that ASTs may need to be refreshed. If the given set is empty, assume
3232
/// that all watched files are affected.
3333
///
3434
/// The callee should refresh ASTs unless it is able to determine that a
3535
/// refresh is not necessary.
36-
func filesDependenciesUpdated(_ changedFiles: Set<DocumentURI>)
36+
func filesDependenciesUpdated(_ changedFiles: Set<DocumentURI>) async
3737

3838
/// Notify the delegate that the file handling capability of this build system
3939
/// for some file has changed. The delegate should discard any cached file
4040
/// handling capability.
41-
func fileHandlingCapabilityChanged()
41+
func fileHandlingCapabilityChanged() async
4242
}

Sources/SKCore/BuildSystemManager.swift

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ extension BuildSystemManager {
200200
if let mainChange = newStatus.buildSettingsChange,
201201
let delegate = self._delegate {
202202
let change = self.convert(change: mainChange, ofMainFile: mainFile, to: uri)
203-
delegate.fileBuildSettingsChanged([uri: change])
203+
await delegate.fileBuildSettingsChanged([uri: change])
204204
}
205205
}
206206

@@ -242,7 +242,7 @@ extension BuildSystemManager {
242242
if let fallback = self.fallbackBuildSystem {
243243
Task {
244244
try await Task.sleep(nanoseconds: UInt64(self.fallbackSettingsTimeout.nanoseconds()!))
245-
self.handleFallbackTimer(for: mainFile, language: language, fallback)
245+
await self.handleFallbackTimer(for: mainFile, language: language, fallback)
246246
}
247247
}
248248

@@ -280,7 +280,7 @@ extension BuildSystemManager {
280280

281281
/// Update and notify our delegate for the given main file changes if they are
282282
/// convertible into `FileBuildSettingsChange`.
283-
func updateAndNotifyStatuses(changes: [DocumentURI: MainFileStatus]) {
283+
func updateAndNotifyStatuses(changes: [DocumentURI: MainFileStatus]) async {
284284
var changedWatchedFiles = [DocumentURI: FileBuildSettingsChange]()
285285
for (mainFile, status) in changes {
286286
let watches = self.watchedFiles.filter { $1.mainFile == mainFile }
@@ -307,7 +307,7 @@ extension BuildSystemManager {
307307
}
308308

309309
if !changedWatchedFiles.isEmpty, let delegate = self._delegate {
310-
delegate.fileBuildSettingsChanged(changedWatchedFiles)
310+
await delegate.fileBuildSettingsChanged(changedWatchedFiles)
311311
}
312312
}
313313

@@ -319,14 +319,14 @@ extension BuildSystemManager {
319319
for mainFile: DocumentURI,
320320
language: Language,
321321
_ fallback: FallbackBuildSystem
322-
) {
322+
) async {
323323
// There won't be a current status if it's unreferenced by any watched file.
324324
// Similarly, if the status isn't `waiting` then there's nothing to do.
325325
guard let status = self.mainFileStatuses[mainFile], status == .waiting else {
326326
return
327327
}
328328
if let settings = fallback.buildSettings(for: mainFile, language: language) {
329-
self.updateAndNotifyStatuses(changes: [mainFile: .waitingUsingFallback(settings)])
329+
await self.updateAndNotifyStatuses(changes: [mainFile: .waitingUsingFallback(settings)])
330330
} else {
331331
// Keep the status as waiting.
332332
}
@@ -367,7 +367,7 @@ extension BuildSystemManager: BuildSystemDelegate {
367367
}
368368
}
369369

370-
public func fileBuildSettingsChangedImpl(_ changes: [DocumentURI: FileBuildSettingsChange]) {
370+
public func fileBuildSettingsChangedImpl(_ changes: [DocumentURI: FileBuildSettingsChange]) async {
371371
let statusChanges: [DocumentURI: MainFileStatus] =
372372
changes.reduce(into: [:]) { (result, entry) in
373373
let mainFile = entry.key
@@ -395,7 +395,7 @@ extension BuildSystemManager: BuildSystemDelegate {
395395
}
396396
result[mainFile] = newStatus
397397
}
398-
self.updateAndNotifyStatuses(changes: statusChanges)
398+
await self.updateAndNotifyStatuses(changes: statusChanges)
399399
}
400400

401401
// FIXME: (async) Make this method isolated once `BuildSystemDelegate` has ben asyncified
@@ -405,11 +405,11 @@ extension BuildSystemManager: BuildSystemDelegate {
405405
}
406406
}
407407

408-
public func filesDependenciesUpdatedImpl(_ changedFiles: Set<DocumentURI>) {
408+
public func filesDependenciesUpdatedImpl(_ changedFiles: Set<DocumentURI>) async {
409409
// Empty changes --> assume everything has changed.
410410
guard !changedFiles.isEmpty else {
411411
if let delegate = self._delegate {
412-
delegate.filesDependenciesUpdated(changedFiles)
412+
await delegate.filesDependenciesUpdated(changedFiles)
413413
}
414414
return
415415
}
@@ -418,7 +418,7 @@ extension BuildSystemManager: BuildSystemDelegate {
418418
let changedWatchedFiles = self.watchedFiles.filter { changedFiles.contains($1.mainFile) }
419419
let newChangedFiles = Set(changedWatchedFiles.map { $0.key })
420420
if let delegate = self._delegate, !newChangedFiles.isEmpty {
421-
delegate.filesDependenciesUpdated(newChangedFiles)
421+
await delegate.filesDependenciesUpdated(newChangedFiles)
422422
}
423423
}
424424

@@ -429,9 +429,9 @@ extension BuildSystemManager: BuildSystemDelegate {
429429
}
430430
}
431431

432-
public func buildTargetsChangedImpl(_ changes: [BuildTargetEvent]) {
432+
public func buildTargetsChangedImpl(_ changes: [BuildTargetEvent]) async {
433433
if let delegate = self._delegate {
434-
delegate.buildTargetsChanged(changes)
434+
await delegate.buildTargetsChanged(changes)
435435
}
436436
}
437437

@@ -442,9 +442,9 @@ extension BuildSystemManager: BuildSystemDelegate {
442442
}
443443
}
444444

445-
public func fileHandlingCapabilityChangedImpl() {
445+
public func fileHandlingCapabilityChangedImpl() async {
446446
if let delegate = self._delegate {
447-
delegate.fileHandlingCapabilityChanged()
447+
await delegate.fileHandlingCapabilityChanged()
448448
}
449449
}
450450
}
@@ -484,7 +484,7 @@ extension BuildSystemManager: MainFilesDelegate {
484484
}
485485

486486
if let delegate = self._delegate, !buildSettingsChanges.isEmpty {
487-
delegate.fileBuildSettingsChanged(buildSettingsChanges)
487+
await delegate.fileBuildSettingsChanged(buildSettingsChanges)
488488
}
489489
}
490490
}

Sources/SKCore/CompilationDatabaseBuildSystem.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,13 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
9191
return self.settings(for: document)
9292
}
9393

94-
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
94+
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) async {
9595
self.watchedFiles[uri] = language
9696

9797
guard let delegate = self.delegate else { return }
9898

9999
let settings = self.settings(for: uri)
100-
delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)])
100+
await delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)])
101101
}
102102

103103
/// We don't support change watching.
@@ -142,7 +142,7 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
142142

143143
/// The compilation database has been changed on disk.
144144
/// Reload it and notify the delegate about build setting changes.
145-
private func reloadCompilationDatabase() {
145+
private func reloadCompilationDatabase() async {
146146
guard let projectRoot = self.projectRoot else { return }
147147

148148
self.compdb = tryLoadCompilationDatabase(directory: projectRoot, self.fileSystem)
@@ -156,13 +156,13 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
156156
changedFiles[uri] = .removedOrUnavailable
157157
}
158158
}
159-
delegate.fileBuildSettingsChanged(changedFiles)
159+
await delegate.fileBuildSettingsChanged(changedFiles)
160160
}
161161
}
162162

163-
public func filesDidChange(_ events: [FileEvent]) {
163+
public func filesDidChange(_ events: [FileEvent]) async {
164164
if events.contains(where: { self.fileEventShouldTriggerCompilationDatabaseReload(event: $0) }) {
165-
self.reloadCompilationDatabase()
165+
await self.reloadCompilationDatabase()
166166
}
167167
}
168168

Sources/SKCore/FallbackBuildSystem.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ public final class FallbackBuildSystem: BuildSystem {
6363
guard let delegate = self.delegate else { return }
6464

6565
let settings = self.buildSettings(for: uri, language: language)
66-
DispatchQueue.global().async {
67-
delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)])
66+
Task {
67+
await delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)])
6868
}
6969
}
7070

Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public actor SwiftPMWorkspace {
152152
self.packageGraph = try PackageGraph(rootPackages: [], dependencies: [], binaryArtifacts: [:])
153153
self.reloadPackageStatusCallback = reloadPackageStatusCallback
154154

155-
try reloadPackage()
155+
try await reloadPackage()
156156
}
157157

158158
/// Creates a build system using the Swift Package Manager, if this workspace is a package.
@@ -188,7 +188,7 @@ extension SwiftPMWorkspace {
188188

189189
/// (Re-)load the package settings by parsing the manifest and resolving all the targets and
190190
/// dependencies.
191-
func reloadPackage() throws {
191+
func reloadPackage() async throws {
192192
reloadPackageStatusCallback(.start)
193193
defer {
194194
reloadPackageStatusCallback(.end)
@@ -252,8 +252,8 @@ extension SwiftPMWorkspace {
252252
}
253253
}
254254
}
255-
delegate.fileBuildSettingsChanged(changedFiles)
256-
delegate.fileHandlingCapabilityChanged()
255+
await delegate.fileBuildSettingsChanged(changedFiles)
256+
await delegate.fileHandlingCapabilityChanged()
257257
}
258258
}
259259

@@ -305,7 +305,7 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
305305
return nil
306306
}
307307

308-
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
308+
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) async {
309309
assert(self.watchedFiles[uri] == nil, "Registered twice for change notifications of the same URI")
310310
guard let delegate = self.delegate else { return }
311311
self.watchedFiles[uri] = language
@@ -317,9 +317,9 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
317317
log("error computing settings: \(error)")
318318
}
319319
if let settings = settings {
320-
delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)])
320+
await delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)])
321321
} else {
322-
delegate.fileBuildSettingsChanged([uri: .removedOrUnavailable])
322+
await delegate.fileBuildSettingsChanged([uri: .removedOrUnavailable])
323323
}
324324
}
325325

@@ -366,11 +366,11 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
366366
}
367367
}
368368

369-
public func filesDidChange(_ events: [FileEvent]) {
369+
public func filesDidChange(_ events: [FileEvent]) async {
370370
if events.contains(where: { self.fileEventShouldTriggerPackageReload(event: $0) }) {
371-
orLog {
371+
await orLog {
372372
// TODO: It should not be necessary to reload the entire package just to get build settings for one file.
373-
try self.reloadPackage()
373+
try await self.reloadPackage()
374374
}
375375
}
376376
}

Sources/SourceKitLSP/Workspace.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public final class Workspace {
9797
) async throws {
9898
var buildSystem: BuildSystem? = nil
9999
if let rootUrl = rootUri.fileURL, let rootPath = try? AbsolutePath(validating: rootUrl.path) {
100-
if let buildServer = BuildServerBuildSystem(projectRoot: rootPath, buildSetup: buildSetup) {
100+
if let buildServer = await BuildServerBuildSystem(projectRoot: rootPath, buildSetup: buildSetup) {
101101
buildSystem = buildServer
102102
} else if let swiftpm = await SwiftPMWorkspace(
103103
url: rootUrl,

0 commit comments

Comments
 (0)