Skip to content

Commit 3ae6501

Browse files
committed
Time out updating the index store after 2 minutes
Time out updating of the index store after 2 minutes. We don't expect any single file compilation to take longer than 2 minutes in practice, so this indicates that the compiler has entered some kind of loop. We will try indexing the file again when it is edited or when the project is re-opened. rdar://128732571
1 parent 84007ca commit 3ae6501

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

Sources/SKSupport/AsyncUtils.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,28 @@ extension Collection where Element: Sendable {
166166
}
167167
}
168168
}
169+
170+
public struct TimeoutError: Error, CustomStringConvertible {
171+
public var description: String { "Timed out" }
172+
}
173+
174+
/// Executes `body`. If it doesn't finish after `duration`, throws a `TimeoutError`.
175+
public func withTimeout<T: Sendable>(
176+
_ duration: Duration,
177+
_ body: @escaping @Sendable () async throws -> T
178+
) async throws -> T {
179+
try await withThrowingTaskGroup(of: T.self) { taskGroup in
180+
taskGroup.addTask {
181+
try await Task.sleep(for: duration)
182+
throw TimeoutError()
183+
}
184+
taskGroup.addTask {
185+
return try await body()
186+
}
187+
for try await value in taskGroup {
188+
taskGroup.cancelAll()
189+
return value
190+
}
191+
throw CancellationError()
192+
}
193+
}

Sources/SemanticIndex/UpdateIndexStoreTaskDescription.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,13 @@ public struct UpdateIndexStoreTaskDescription: IndexTaskDescription {
321321
arguments: processArguments,
322322
workingDirectory: workingDirectory
323323
)
324-
let result = try await process.waitUntilExitSendingSigIntOnTaskCancellation()
324+
// Time out updating of the index store after 2 minutes. We don't expect any single file compilation to take longer
325+
// than 2 minutes in practice, so this indicates that the compiler has entered a loop and we probably won't make any
326+
// progress here. We will try indexing the file again when it is edited or when the project is re-opened.
327+
// 2 minutes have been chosen arbitrarily.
328+
let result = try await withTimeout(.seconds(120)) {
329+
try await process.waitUntilExitSendingSigIntOnTaskCancellation()
330+
}
325331

326332
indexProcessDidProduceResult(
327333
IndexProcessResult(
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 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 LSPTestSupport
14+
import SKSupport
15+
import XCTest
16+
17+
final class AsyncUtilsTests: XCTestCase {
18+
func testWithTimeout() async throws {
19+
let expectation = self.expectation(description: "withTimeout body finished")
20+
await assertThrowsError(
21+
try await withTimeout(.seconds(0.1)) {
22+
try? await Task.sleep(for: .seconds(10))
23+
XCTAssert(Task.isCancelled)
24+
expectation.fulfill()
25+
}
26+
) { error in
27+
XCTAssert(error is TimeoutError, "Received unexpected error \(error)")
28+
}
29+
try await fulfillmentOfOrThrow([expectation])
30+
}
31+
}

0 commit comments

Comments
 (0)