Skip to content

Commit 76847db

Browse files
committed
Do not try to infer build settings from a sibling file if determining the target timed out
1 parent c360cfa commit 76847db

File tree

2 files changed

+67
-33
lines changed

2 files changed

+67
-33
lines changed

Sources/BuildServerIntegration/BuildServerManager.swift

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,21 +1205,23 @@ package actor BuildServerManager: QueueBasedMessageHandler {
12051205
basedOn document: DocumentURI
12061206
) async -> (mainFile: DocumentURI, settings: FileBuildSettings)? {
12071207
let mainFile = await self.mainFile(for: document, language: language)
1208-
let settings: FileBuildSettings? = await orLog("Getting build settings") {
1209-
let target =
1208+
let settings: FileBuildSettings? = await orLog("Getting build settings") { () -> FileBuildSettings? in
1209+
let target: WithTimeoutResult<BuildTargetIdentifier?> =
12101210
if let explicitlyRequestedTarget {
1211-
explicitlyRequestedTarget
1211+
.result(explicitlyRequestedTarget)
12121212
} else {
1213-
try await withTimeout(options.buildSettingsTimeoutOrDefault) {
1214-
await self.canonicalTarget(for: mainFile)
1213+
try await withTimeoutResult(options.buildSettingsTimeoutOrDefault) {
1214+
return await self.canonicalTarget(for: mainFile)
12151215
} resultReceivedAfterTimeout: { _ in
12161216
await self.filesBuildSettingsChangedDebouncer.scheduleCall([document])
12171217
}
12181218
}
12191219
var languageForFile: Language
12201220
if let language {
12211221
languageForFile = language
1222-
} else if let target, let language = await self.defaultLanguage(for: mainFile, in: target) {
1222+
} else if case let .result(target?) = target,
1223+
let language = await self.defaultLanguage(for: mainFile, in: target)
1224+
{
12231225
languageForFile = language
12241226
} else if let language = Language(inferredFromFileExtension: mainFile) {
12251227
languageForFile = language
@@ -1228,32 +1230,38 @@ package actor BuildServerManager: QueueBasedMessageHandler {
12281230
// settings.
12291231
return nil
12301232
}
1231-
guard let target else {
1233+
switch target {
1234+
case .result(let target?):
1235+
return await self.buildSettings(
1236+
for: mainFile,
1237+
in: target,
1238+
language: languageForFile,
1239+
fallbackAfterTimeout: fallbackAfterTimeout
1240+
)
1241+
case .result(nil):
1242+
if allowInferenceFromSiblingFile {
1243+
let settingsFromSibling = await orLog("Inferring build settings from sibling file") {
1244+
try await self.fallbackBuildSettingsInferredFromSiblingFile(
1245+
of: document,
1246+
target: explicitlyRequestedTarget,
1247+
language: language,
1248+
fallbackAfterTimeout: fallbackAfterTimeout
1249+
)
1250+
}
1251+
if let settingsFromSibling {
1252+
return settingsFromSibling
1253+
}
1254+
}
1255+
fallthrough
1256+
case .timedOut:
1257+
// If we timed out, we don't want to try inferring the build settings from a sibling since that would kick off
1258+
// new requests to the build server, which will likely also time out.
12321259
return fallbackBuildSettings(
12331260
for: document,
12341261
language: languageForFile,
12351262
options: options.fallbackBuildSystemOrDefault
12361263
)
12371264
}
1238-
return await self.buildSettings(
1239-
for: mainFile,
1240-
in: target,
1241-
language: languageForFile,
1242-
fallbackAfterTimeout: fallbackAfterTimeout
1243-
)
1244-
}
1245-
if settings?.isFallback ?? true, allowInferenceFromSiblingFile {
1246-
let settingsFromSibling = await orLog("Inferring build settings from sibling file") {
1247-
try await self.fallbackBuildSettingsInferredFromSiblingFile(
1248-
of: document,
1249-
target: explicitlyRequestedTarget,
1250-
language: language,
1251-
fallbackAfterTimeout: fallbackAfterTimeout
1252-
)
1253-
}
1254-
if let settingsFromSibling {
1255-
return (mainFile, settingsFromSibling)
1256-
}
12571265
}
12581266
guard let settings else {
12591267
return nil

Sources/SwiftExtensions/AsyncUtils.swift

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,23 +263,28 @@ package func withTimeout<T: Sendable>(
263263
}
264264
}
265265

266-
/// Executes `body`. If it doesn't finish after `duration`, return `nil` and continue running body. When `body` returns
267-
/// a value after the timeout, `resultReceivedAfterTimeout` is called.
266+
package enum WithTimeoutResult<T> {
267+
case result(T)
268+
case timedOut
269+
}
270+
271+
/// Executes `body`. If it doesn't finish after `duration`, return `.timed` and continue running body. When `body`
272+
/// returns a value after the timeout, `resultReceivedAfterTimeout` is called.
268273
///
269274
/// - Important: `body` will not be cancelled when the timeout is received. Use the other overload of `withTimeout` if
270275
/// `body` should be cancelled after `timeout`.
271-
package func withTimeout<T: Sendable>(
276+
package func withTimeoutResult<T: Sendable>(
272277
_ timeout: Duration,
273278
body: @escaping @Sendable () async throws -> T,
274279
resultReceivedAfterTimeout: @escaping @Sendable (_ result: T) async -> Void
275-
) async throws -> T? {
280+
) async throws -> WithTimeoutResult<T> {
276281
let didHitTimeout = AtomicBool(initialValue: false)
277282

278-
let stream = AsyncThrowingStream<T?, Error> { continuation in
283+
let stream = AsyncThrowingStream<WithTimeoutResult<T>, Error> { continuation in
279284
Task {
280285
try await Task.sleep(for: timeout)
281286
didHitTimeout.value = true
282-
continuation.yield(nil)
287+
continuation.yield(.timedOut)
283288
}
284289

285290
Task {
@@ -288,7 +293,7 @@ package func withTimeout<T: Sendable>(
288293
if didHitTimeout.value {
289294
await resultReceivedAfterTimeout(result)
290295
}
291-
continuation.yield(result)
296+
continuation.yield(.result(result))
292297
} catch {
293298
continuation.yield(with: .failure(error))
294299
}
@@ -307,6 +312,27 @@ package func withTimeout<T: Sendable>(
307312
}
308313
}
309314

315+
/// Executes `body`. If it doesn't finish after `duration`, return `nil` and continue running body. When `body` returns
316+
/// a value after the timeout, `resultReceivedAfterTimeout` is called.
317+
///
318+
/// - Important: `body` will not be cancelled when the timeout is received. Use the other overload of `withTimeout` if
319+
/// `body` should be cancelled after `timeout`.
320+
package func withTimeout<T: Sendable>(
321+
_ timeout: Duration,
322+
body: @escaping @Sendable () async throws -> T,
323+
resultReceivedAfterTimeout: @escaping @Sendable (_ result: T) async -> Void
324+
) async throws -> T? {
325+
let timeoutResult: WithTimeoutResult<T> = try await withTimeoutResult(
326+
timeout,
327+
body: body,
328+
resultReceivedAfterTimeout: resultReceivedAfterTimeout
329+
)
330+
switch timeoutResult {
331+
case .timedOut: return nil
332+
case .result(let result): return result
333+
}
334+
}
335+
310336
/// Same as `withTimeout` above but allows `body` to return an optional value.
311337
package func withTimeout<T: Sendable>(
312338
_ timeout: Duration,

0 commit comments

Comments
 (0)