Skip to content

Commit c93f193

Browse files
committed
Share message handling logic between BuiltInBuildSystemAdapter and BuildSystemManager
1 parent 6cc2cc4 commit c93f193

File tree

4 files changed

+117
-143
lines changed

4 files changed

+117
-143
lines changed

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 6 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ fileprivate class RequestCache<Request: RequestType & Hashable, Result: Sendable
7070
/// Since some `BuildSystem`s may require a bit of a time to compute their arguments asynchronously,
7171
/// this class has a configurable `buildSettings` timeout which denotes the amount of time to give
7272
/// the build system before applying the fallback arguments.
73-
package actor BuildSystemManager: MessageHandler {
73+
package actor BuildSystemManager: QueueBasedMessageHandler {
74+
package static let signpostLoggingCategory: String = "build-system-manager-message-handling"
75+
7476
/// The files for which the delegate has requested change notifications, ie.
7577
/// the files for which the delegate wants to get `filesDependenciesUpdated`
7678
/// callbacks if the file's build settings.
@@ -262,24 +264,9 @@ package actor BuildSystemManager: MessageHandler {
262264
}
263265

264266
// FIXME: (BSP Migration) Can we use more fine-grained dependency tracking here?
265-
private let messageHandlingQueue = AsyncQueue<Serial>()
267+
package let messageHandlingQueue = AsyncQueue<Serial>()
266268

267-
/// Implementation of `MessageHandler`, handling notifications from the build system.
268-
///
269-
/// - Important: Do not call directly.
270-
nonisolated package func handle(_ notification: some NotificationType) {
271-
let signposter = Logger(subsystem: LoggingScope.subsystem, category: "build-system-manager-message-handling")
272-
.makeSignposter()
273-
let signpostID = signposter.makeSignpostID()
274-
let state = signposter.beginInterval("Notification", id: signpostID, "\(type(of: notification))")
275-
messageHandlingQueue.async {
276-
signposter.emitEvent("Start handling", id: signpostID)
277-
await self.handleImpl(notification)
278-
signposter.endInterval("Notification", state, "Done")
279-
}
280-
}
281-
282-
private func handleImpl(_ notification: some NotificationType) async {
269+
package func handleImpl(_ notification: some NotificationType) async {
283270
switch notification {
284271
case let notification as DidChangeBuildTargetNotification:
285272
await self.didChangeBuildTarget(notification: notification)
@@ -292,62 +279,7 @@ package actor BuildSystemManager: MessageHandler {
292279
}
293280
}
294281

295-
/// Implementation of `MessageHandler`, handling requests from the build system.
296-
///
297-
/// - Important: Do not call directly.
298-
nonisolated package func handle<R: RequestType>(
299-
_ params: R,
300-
id: RequestID,
301-
reply: @Sendable @escaping (LSPResult<R.Response>) -> Void
302-
) {
303-
let signposter = Logger(subsystem: LoggingScope.subsystem, category: "build-system-message-handling")
304-
.makeSignposter()
305-
let signpostID = signposter.makeSignpostID()
306-
let state = signposter.beginInterval("Request", id: signpostID, "\(R.self)")
307-
308-
messageHandlingQueue.async {
309-
signposter.emitEvent("Start handling", id: signpostID)
310-
await withTaskCancellationHandler {
311-
await self.handleImpl(params, id: id, reply: reply)
312-
signposter.endInterval("Request", state, "Done")
313-
} onCancel: {
314-
signposter.emitEvent("Cancelled", id: signpostID)
315-
}
316-
}
317-
}
318-
319-
private func handleImpl<Request: RequestType>(
320-
_ request: Request,
321-
id: RequestID,
322-
reply: @escaping @Sendable (LSPResult<Request.Response>) -> Void
323-
) async {
324-
let startDate = Date()
325-
326-
let request = RequestAndReply(request) { result in
327-
reply(result)
328-
let endDate = Date()
329-
Task {
330-
switch result {
331-
case .success(let response):
332-
logger.log(
333-
"""
334-
Succeeded (took \(endDate.timeIntervalSince(startDate) * 1000, privacy: .public)ms)
335-
\(Request.method, privacy: .public)
336-
\(response.forLogging)
337-
"""
338-
)
339-
case .failure(let error):
340-
logger.log(
341-
"""
342-
Failed (took \(endDate.timeIntervalSince(startDate) * 1000, privacy: .public)ms)
343-
\(Request.method, privacy: .public)(\(id, privacy: .public))
344-
\(error.forLogging, privacy: .private)
345-
"""
346-
)
347-
}
348-
}
349-
}
350-
282+
package func handleImpl<Request: RequestType>(_ request: RequestAndReply<Request>) async {
351283
switch request {
352284
case let request as RequestAndReply<BuildServerProtocol.CreateWorkDoneProgressRequest>:
353285
await request.reply { try await self.createWorkDoneProgress(request: request.params) }

Sources/BuildSystemIntegration/BuiltInBuildSystemAdapter.swift

Lines changed: 5 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,16 @@ private func createBuildSystem(
7272

7373
/// A type that outwardly acts as a build server conforming to the Build System Integration Protocol and internally uses
7474
/// a `BuiltInBuildSystem` to satisfy the requests.
75-
package actor BuiltInBuildSystemAdapter: MessageHandler {
75+
package actor BuiltInBuildSystemAdapter: QueueBasedMessageHandler {
76+
package static let signpostLoggingCategory: String = "build-system-message-handling"
7677
/// The underlying build system
7778
// FIXME: (BSP Migration) This should be private, all messages should go through BSP. Only accessible from the outside for transition
7879
// purposes.
7980
private(set) package var underlyingBuildSystem: BuiltInBuildSystem!
8081
private let connectionToSourceKitLSP: LocalConnection
8182

8283
// FIXME: (BSP migration) Can we have more fine-grained dependency tracking here?
83-
private let messageHandlingQueue = AsyncQueue<Serial>()
84+
package let messageHandlingQueue = AsyncQueue<Serial>()
8485

8586
init?(
8687
buildSystemKind: BuildSystemKind?,
@@ -123,19 +124,7 @@ package actor BuiltInBuildSystemAdapter: MessageHandler {
123124
)
124125
}
125126

126-
nonisolated package func handle(_ notification: some NotificationType) {
127-
let signposter = Logger(subsystem: LoggingScope.subsystem, category: "build-system-message-handling")
128-
.makeSignposter()
129-
let signpostID = signposter.makeSignpostID()
130-
let state = signposter.beginInterval("Notification", id: signpostID, "\(type(of: notification))")
131-
messageHandlingQueue.async {
132-
signposter.emitEvent("Start handling", id: signpostID)
133-
await self.handleImpl(notification)
134-
signposter.endInterval("Notification", state, "Done")
135-
}
136-
}
137-
138-
private func handleImpl(_ notification: some NotificationType) async {
127+
package func handleImpl(_ notification: some NotificationType) async {
139128
switch notification {
140129
case let notification as DidChangeWatchedFilesNotification:
141130
await self.underlyingBuildSystem.didChangeWatchedFiles(notification: notification)
@@ -144,60 +133,7 @@ package actor BuiltInBuildSystemAdapter: MessageHandler {
144133
}
145134
}
146135

147-
package nonisolated func handle<R: RequestType>(
148-
_ params: R,
149-
id: RequestID,
150-
reply: @Sendable @escaping (LSPResult<R.Response>) -> Void
151-
) {
152-
// FIXME: Can we share this between the different message handler implementations?
153-
let signposter = Logger(subsystem: LoggingScope.subsystem, category: "build-system-message-handling")
154-
.makeSignposter()
155-
let signpostID = signposter.makeSignpostID()
156-
let state = signposter.beginInterval("Request", id: signpostID, "\(R.self)")
157-
158-
messageHandlingQueue.async {
159-
signposter.emitEvent("Start handling", id: signpostID)
160-
await withTaskCancellationHandler {
161-
await self.handleImpl(params, id: id, reply: reply)
162-
signposter.endInterval("Request", state, "Done")
163-
} onCancel: {
164-
signposter.emitEvent("Cancelled", id: signpostID)
165-
}
166-
}
167-
}
168-
169-
private func handleImpl<Request: RequestType>(
170-
_ request: Request,
171-
id: RequestID,
172-
reply: @escaping @Sendable (LSPResult<Request.Response>) -> Void
173-
) async {
174-
let startDate = Date()
175-
176-
let request = RequestAndReply(request) { result in
177-
reply(result)
178-
let endDate = Date()
179-
Task {
180-
switch result {
181-
case .success(let response):
182-
logger.log(
183-
"""
184-
Succeeded (took \(endDate.timeIntervalSince(startDate) * 1000, privacy: .public)ms)
185-
\(Request.method, privacy: .public)
186-
\(response.forLogging)
187-
"""
188-
)
189-
case .failure(let error):
190-
logger.log(
191-
"""
192-
Failed (took \(endDate.timeIntervalSince(startDate) * 1000, privacy: .public)ms)
193-
\(Request.method, privacy: .public)(\(id, privacy: .public))
194-
\(error.forLogging, privacy: .private)
195-
"""
196-
)
197-
}
198-
}
199-
}
200-
136+
package func handleImpl<Request: RequestType>(_ request: RequestAndReply<Request>) async {
201137
switch request {
202138
case let request as RequestAndReply<BuildTargetsRequest>:
203139
await request.reply { try await underlyingBuildSystem.buildTargets(request: request.params) }

Sources/BuildSystemIntegration/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ add_library(BuildSystemIntegration STATIC
1313
IndexTaskID.swift
1414
MainFilesProvider.swift
1515
PathPrefixMapping.swift
16+
QueueBasedMessageHandler.swift
1617
SplitShellCommand.swift
1718
SwiftPMBuildSystem.swift
1819
TestBuildSystem.swift)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Foundation
14+
import LanguageServerProtocol
15+
import SKLogging
16+
import SKSupport
17+
import SwiftExtensions
18+
19+
/// A `MessageHandler` that handles all messages on a serial queue.
20+
///
21+
/// This is a slightly simplified version of the message handling in `SourceKitLSPServer`, which does not set logging
22+
/// scopes, because the build system messages should still be logged in the scope of the original LSP request that
23+
/// triggered them.
24+
protocol QueueBasedMessageHandler: MessageHandler {
25+
var messageHandlingQueue: AsyncQueue<Serial> { get }
26+
27+
static var signpostLoggingCategory: String { get }
28+
29+
func handleImpl(_ notification: some NotificationType) async
30+
31+
func handleImpl<Request: RequestType>(_ requestAndReply: RequestAndReply<Request>) async
32+
}
33+
34+
extension QueueBasedMessageHandler {
35+
/// Handle a notification without a reply.
36+
///
37+
/// The method should return as soon as the notification has been sufficiently
38+
/// handled to avoid out-of-order requests, e.g. once the notification has
39+
/// been forwarded to clangd.
40+
package func handle(_ notification: some NotificationType) {
41+
let signposter = Logger(subsystem: LoggingScope.subsystem, category: Self.signpostLoggingCategory)
42+
.makeSignposter()
43+
let signpostID = signposter.makeSignpostID()
44+
let state = signposter.beginInterval("Notification", id: signpostID, "\(type(of: notification))")
45+
messageHandlingQueue.async {
46+
signposter.emitEvent("Start handling", id: signpostID)
47+
await self.handleImpl(notification)
48+
signposter.endInterval("Notification", state, "Done")
49+
}
50+
}
51+
52+
/// Handle a request and (asynchronously) receive a reply.
53+
///
54+
/// The method should return as soon as the request has been sufficiently
55+
/// handled to avoid out-of-order requests, e.g. once the corresponding
56+
/// request has been sent to sourcekitd. The actual semantic computation
57+
/// should occur after the method returns and report the result via `reply`.
58+
package func handle<Request: RequestType>(
59+
_ request: Request,
60+
id: RequestID,
61+
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
62+
) {
63+
let signposter = Logger(subsystem: LoggingScope.subsystem, category: Self.signpostLoggingCategory)
64+
.makeSignposter()
65+
let signpostID = signposter.makeSignpostID()
66+
let state = signposter.beginInterval("Request", id: signpostID, "\(Request.self)")
67+
68+
messageHandlingQueue.async {
69+
signposter.emitEvent("Start handling", id: signpostID)
70+
await withTaskCancellationHandler {
71+
let startDate = Date()
72+
73+
let requestAndReply = RequestAndReply(request) { result in
74+
reply(result)
75+
let endDate = Date()
76+
Task {
77+
switch result {
78+
case .success(let response):
79+
logger.log(
80+
"""
81+
Succeeded (took \(endDate.timeIntervalSince(startDate) * 1000, privacy: .public)ms)
82+
\(Request.method, privacy: .public)
83+
\(response.forLogging)
84+
"""
85+
)
86+
case .failure(let error):
87+
logger.log(
88+
"""
89+
Failed (took \(endDate.timeIntervalSince(startDate) * 1000, privacy: .public)ms)
90+
\(Request.method, privacy: .public)(\(id, privacy: .public))
91+
\(error.forLogging, privacy: .private)
92+
"""
93+
)
94+
}
95+
}
96+
}
97+
98+
await self.handleImpl(requestAndReply)
99+
signposter.endInterval("Request", state, "Done")
100+
} onCancel: {
101+
signposter.emitEvent("Cancelled", id: signpostID)
102+
}
103+
}
104+
}
105+
}

0 commit comments

Comments
 (0)