Skip to content

Commit 9f09285

Browse files
authored
Merge pull request #2245 from benpious/switch-to-async
Fix a minor threading issue and TODO by switching to structured concurrency
2 parents 1b7b2db + 8a9ecd5 commit 9f09285

File tree

1 file changed

+50
-45
lines changed

1 file changed

+50
-45
lines changed

CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ struct TemplateSpec {
6868
}
6969

7070
@main
71-
struct GenerateSwiftSyntax: ParsableCommand {
71+
struct GenerateSwiftSyntax: AsyncParsableCommand {
7272
@Argument(help: "The path to the source directory (i.e. 'swift-syntax/Sources') where the source files are to be generated")
7373
var destination: String = {
7474
let sourcesURL = URL(fileURLWithPath: #filePath)
@@ -83,7 +83,7 @@ struct GenerateSwiftSyntax: ParsableCommand {
8383
@Flag(help: "Enable verbose output")
8484
var verbose: Bool = false
8585

86-
func run() throws {
86+
func run() async throws {
8787
let destination = URL(fileURLWithPath: self.destination).standardizedFileURL
8888

8989
var fileSpecs: [GeneratedFileSpec] = [
@@ -139,7 +139,6 @@ struct GenerateSwiftSyntax: ParsableCommand {
139139

140140
let modules = Set(fileSpecs.compactMap { $0.pathComponents.first })
141141

142-
let previouslyGeneratedFilesLock = NSLock()
143142
var previouslyGeneratedFiles = Set(
144143
modules.flatMap { (module) -> [URL] in
145144
let generatedDir =
@@ -154,32 +153,37 @@ struct GenerateSwiftSyntax: ParsableCommand {
154153
}
155154
)
156155

157-
var errors: [Error] = []
158-
DispatchQueue.concurrentPerform(iterations: fileSpecs.count) { index in
159-
let fileSpec = fileSpecs[index]
160-
do {
161-
var destination = destination
162-
for component in fileSpec.pathComponents {
163-
destination = destination.appendingPathComponent(component)
156+
await withTaskGroup(of: (url: URL, error: Error?).self) { group in
157+
for fileSpec in fileSpecs {
158+
group.addTask {
159+
do {
160+
var destination = destination
161+
for component in fileSpec.pathComponents {
162+
destination = destination.appendingPathComponent(component)
163+
}
164+
do {
165+
try generateFile(
166+
contents: fileSpec.contents,
167+
destination: destination,
168+
verbose: verbose
169+
)
170+
} catch {
171+
// If we throw from here, we'll lose the URL,
172+
// and we'll end up removing a file that is still
173+
// included in the files we intend to generate,
174+
// even if we failed to do so on this run.
175+
return (destination, error)
176+
}
177+
return (destination, nil)
178+
}
179+
}
180+
}
181+
for await result in group {
182+
_ = previouslyGeneratedFiles.remove(result.url)
183+
if let error = result.error {
184+
print("Error when generating file at \(result.url):", error)
164185
}
165-
166-
previouslyGeneratedFilesLock.lock();
167-
_ = previouslyGeneratedFiles.remove(destination)
168-
previouslyGeneratedFilesLock.unlock()
169-
170-
try generateFile(
171-
contents: fileSpec.contents,
172-
destination: destination,
173-
verbose: verbose
174-
)
175-
} catch {
176-
errors.append(error)
177186
}
178-
}
179-
180-
if let firstError = errors.first {
181-
// TODO: It would be nice if we could emit all errors
182-
throw firstError
183187
}
184188

185189
for file in previouslyGeneratedFiles {
@@ -189,24 +193,25 @@ struct GenerateSwiftSyntax: ParsableCommand {
189193
}
190194
}
191195

192-
private func generateFile(
193-
contents: @autoclosure () -> String,
194-
destination: URL,
195-
verbose: Bool
196-
) throws {
197-
try FileManager.default.createDirectory(
198-
atPath: destination.deletingLastPathComponent().path,
199-
withIntermediateDirectories: true,
200-
attributes: nil
201-
)
196+
}
202197

203-
if verbose {
204-
print("Generating \(destination.path)...")
205-
}
206-
let start = Date()
207-
try contents().write(to: destination, atomically: true, encoding: .utf8)
208-
if verbose {
209-
print("Generated \(destination.path) in \((Date().timeIntervalSince(start) * 1000).rounded() / 1000)s")
210-
}
198+
fileprivate func generateFile(
199+
contents: @autoclosure () -> String,
200+
destination: URL,
201+
verbose: Bool
202+
) throws {
203+
try FileManager.default.createDirectory(
204+
atPath: destination.deletingLastPathComponent().path,
205+
withIntermediateDirectories: true,
206+
attributes: nil
207+
)
208+
209+
if verbose {
210+
print("Generating \(destination.path)...")
211+
}
212+
let start = Date()
213+
try contents().write(to: destination, atomically: true, encoding: .utf8)
214+
if verbose {
215+
print("Generated \(destination.path) in \((Date().timeIntervalSince(start) * 1000).rounded() / 1000)s")
211216
}
212217
}

0 commit comments

Comments
 (0)