Skip to content

Commit 6bed023

Browse files
authored
Avoid using temp_await on loadRootPackage (#7009)
### Motivation: Using locks to convert asynchronous functions to synchronous is dangerous, since this can deadlock the Swift Concurrency thread pool. Unfortunately, `temp_await` (AKA `tsc_await`) function utilizes exactly this approach. As SourceKit-LSP starts adopting Swift Concurrency (swiftlang/sourcekit-lsp#850), we should avoid using `temp_await` in our codebase and make the callers of corresponding `async` or callback-taking functions themselves `async`. Otherwise there's a risk of deadlocks in SourceKit-LSP itself, since their Swift Concurrency tasks may be blocked by our uses of `temp_await` under the hood. ### Modifications: `loadRootPackage` already has an `async` overload in `Workspace`. Converted callers in SwiftPM commands to use `AsyncSwiftCommand` protocol instead of `SwiftCommand`. Also, test cases were made `async` too. ### Result: Reduced the number of `temp_await` uses in `Source`. rdar://79350642
1 parent 7a576ed commit 6bed023

File tree

10 files changed

+110
-108
lines changed

10 files changed

+110
-108
lines changed

.swiftpm/xcode/xcshareddata/xcschemes/SwiftPM-Package.xcscheme

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,16 @@
839839
ReferencedContainer = "container:">
840840
</BuildableReference>
841841
</TestableReference>
842+
<TestableReference
843+
skipped = "NO">
844+
<BuildableReference
845+
BuildableIdentifier = "primary"
846+
BlueprintIdentifier = "SourceKitLSPAPITests"
847+
BuildableName = "SourceKitLSPAPITests"
848+
BlueprintName = "SourceKitLSPAPITests"
849+
ReferencedContainer = "container:">
850+
</BuildableReference>
851+
</TestableReference>
842852
</Testables>
843853
</TestAction>
844854
<LaunchAction

Sources/Commands/PackageTools/Describe.swift

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import PackageModel
1919
import struct TSCBasic.StringError
2020

2121
extension SwiftPackageTool {
22-
struct Describe: SwiftCommand {
22+
struct Describe: AsyncSwiftCommand {
2323
static let configuration = CommandConfiguration(
2424
abstract: "Describe the current package")
2525

@@ -29,21 +29,18 @@ extension SwiftPackageTool {
2929
@Option(help: "json | text")
3030
var type: DescribeMode = .text
3131

32-
func run(_ swiftTool: SwiftTool) throws {
32+
func run(_ swiftTool: SwiftTool) async throws {
3333
let workspace = try swiftTool.getActiveWorkspace()
3434

3535
guard let packagePath = try swiftTool.getWorkspaceRoot().packages.first else {
3636
throw StringError("unknown package")
3737
}
3838

39-
let package = try temp_await {
40-
workspace.loadRootPackage(
41-
at: packagePath,
42-
observabilityScope: swiftTool.observabilityScope,
43-
completion: $0
44-
)
45-
}
46-
39+
let package = try await workspace.loadRootPackage(
40+
at: packagePath,
41+
observabilityScope: swiftTool.observabilityScope
42+
)
43+
4744
try self.describe(package, in: type)
4845
}
4946

Sources/Commands/PackageTools/DumpCommands.swift

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,24 +97,21 @@ enum ExtensionBlockSymbolBehavior: String, EnumerableFlag {
9797
case omitExtensionBlockSymbols
9898
}
9999

100-
struct DumpPackage: SwiftCommand {
100+
struct DumpPackage: AsyncSwiftCommand {
101101
static let configuration = CommandConfiguration(
102102
abstract: "Print parsed Package.swift as JSON")
103103

104104
@OptionGroup(visibility: .hidden)
105105
var globalOptions: GlobalOptions
106106

107-
func run(_ swiftTool: SwiftTool) throws {
107+
func run(_ swiftTool: SwiftTool) async throws {
108108
let workspace = try swiftTool.getActiveWorkspace()
109109
let root = try swiftTool.getWorkspaceRoot()
110110

111-
let rootManifests = try temp_await {
112-
workspace.loadRootManifests(
113-
packages: root.packages,
114-
observabilityScope: swiftTool.observabilityScope,
115-
completion: $0
116-
)
117-
}
111+
let rootManifests = try await workspace.loadRootManifests(
112+
packages: root.packages,
113+
observabilityScope: swiftTool.observabilityScope
114+
)
118115
guard let rootManifest = rootManifests.values.first else {
119116
throw StringError("invalid manifests at \(root.packages)")
120117
}

Sources/Commands/PackageTools/Format.swift

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import enum TSCBasic.ProcessEnv
2121
import enum TSCUtility.Diagnostics
2222

2323
extension SwiftPackageTool {
24-
struct Format: SwiftCommand {
24+
struct Format: AsyncSwiftCommand {
2525
static let configuration = CommandConfiguration(
2626
commandName: "_format", shouldDisplay: false)
2727

@@ -32,7 +32,7 @@ extension SwiftPackageTool {
3232
help: "Pass flag through to the swift-format tool")
3333
var swiftFormatFlags: [String] = []
3434

35-
func run(_ swiftTool: SwiftTool) throws {
35+
func run(_ swiftTool: SwiftTool) async throws {
3636
// Look for swift-format binary.
3737
// FIXME: This should be moved to user toolchain.
3838
let swiftFormatInEnv = lookupExecutablePath(filename: ProcessEnv.vars["SWIFT_FORMAT"])
@@ -48,13 +48,11 @@ extension SwiftPackageTool {
4848
throw StringError("unknown package")
4949
}
5050

51-
let package = try temp_await {
52-
workspace.loadRootPackage(
53-
at: packagePath,
54-
observabilityScope: swiftTool.observabilityScope,
55-
completion: $0
56-
)
57-
}
51+
let package = try await workspace.loadRootPackage(
52+
at: packagePath,
53+
observabilityScope: swiftTool.observabilityScope
54+
)
55+
5856

5957
// Use the user provided flags or default to formatting mode.
6058
let formatOptions = swiftFormatFlags.isEmpty
@@ -71,7 +69,7 @@ extension SwiftPackageTool {
7169
let args = [swiftFormat.pathString] + formatOptions + [packagePath.pathString] + paths
7270
print("Running:", args.map{ $0.spm_shellEscaped() }.joined(separator: " "))
7371

74-
let result = try TSCBasic.Process.popen(arguments: args)
72+
let result = try await TSCBasic.Process.popen(arguments: args)
7573
let output = try (result.utf8Output() + result.utf8stderrOutput())
7674

7775
if result.exitStatus != .terminated(code: 0) {

Sources/Commands/PackageTools/SwiftPackageTool.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import XCBuildSupport
2525
import enum TSCUtility.Diagnostics
2626

2727
/// swift-package tool namespace
28-
public struct SwiftPackageTool: ParsableCommand {
28+
public struct SwiftPackageTool: AsyncParsableCommand {
2929
public static var configuration = CommandConfiguration(
3030
commandName: "package",
3131
_superCommandName: "swift",

Sources/SPMTestSupport/MockWorkspace.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,13 +510,16 @@ public final class MockWorkspace {
510510
public let diagnostics: [Basics.Diagnostic]
511511
}
512512

513-
public func checkPrecomputeResolution(_ check: (ResolutionPrecomputationResult) -> Void) throws {
513+
public func checkPrecomputeResolution() async throws -> ResolutionPrecomputationResult {
514514
let observability = ObservabilitySystem.makeForTesting()
515515
let workspace = try self.getOrCreateWorkspace()
516516
let pinsStore = try workspace.pinsStore.load()
517517

518518
let rootInput = PackageGraphRootInput(packages: try rootPaths(for: roots.map { $0.name }), dependencies: [])
519-
let rootManifests = try temp_await { workspace.loadRootManifests(packages: rootInput.packages, observabilityScope: observability.topScope, completion: $0) }
519+
let rootManifests = try await workspace.loadRootManifests(
520+
packages: rootInput.packages,
521+
observabilityScope: observability.topScope
522+
)
520523
let root = PackageGraphRoot(input: rootInput, manifests: rootManifests, observabilityScope: observability.topScope)
521524

522525
let dependencyManifests = try workspace.loadDependencyManifests(root: root, observabilityScope: observability.topScope)
@@ -529,7 +532,7 @@ public final class MockWorkspace {
529532
observabilityScope: observability.topScope
530533
)
531534

532-
check(ResolutionPrecomputationResult(result: result, diagnostics: observability.diagnostics))
535+
return ResolutionPrecomputationResult(result: result, diagnostics: observability.diagnostics)
533536
}
534537

535538
public func set(

Sources/swift-package-manager/SwiftPM.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct SwiftPM {
2525
static func main() async {
2626
switch execName {
2727
case "swift-package":
28-
SwiftPackageTool.main()
28+
await SwiftPackageTool.main()
2929
case "swift-build":
3030
SwiftBuildTool.main()
3131
case "swift-experimental-sdk":

Sources/swift-package/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
add_executable(swift-package
10-
main.swift)
10+
Entrypoint.swift)
1111
target_link_libraries(swift-package PRIVATE
1212
Commands
1313
TSCBasic)
1414

15+
target_compile_options(swift-package PRIVATE
16+
-parse-as-library)
17+
1518
install(TARGETS swift-package
1619
RUNTIME DESTINATION bin)

Sources/swift-package/main.swift renamed to Sources/swift-package/Entrypoint.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,9 @@
1212

1313
import Commands
1414

15-
SwiftPackageTool.main()
15+
@main
16+
struct Entrypoint {
17+
static func main() async {
18+
await SwiftPackageTool.main()
19+
}
20+
}

0 commit comments

Comments
 (0)