Skip to content

Commit 00feb23

Browse files
authored
[6.1] Frequent crashes in LLBuildProgressTracker while building swift-java on linux and swift 6.1.2 (#9019)
### Explanation: (cherry picked from commit 16452e1) Frequent crashes in LLBuildProgressTracker while building swift-java on linux and swift 6.1.2 This was a race condition because the Command is associated with a handle (pointer) that may be reclaimed when this function finishes. ### Scope: Minimal, copy variables into locals before accessing them from async block. ### Issues: swiftlang/swift-java#314 almost all CI runs fail with this race condition due to the timing of the command being influenced by the gradle build in swift-java. ### Original PRs: #8998 ### Risk: Minimal. Change explicitly copies variables prior to usage in async block. This avoided a race condition that would manifest before as null pointer crashes when trying to access these variables. Change has been tested in main and 6.2 ### Testing: Confirmed fixed in main and 6.2. Manually verified in always reproducing project.
1 parent 45c31f1 commit 00feb23

File tree

1 file changed

+21
-15
lines changed

1 file changed

+21
-15
lines changed

Sources/Build/LLBuildProgressTracker.swift

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ final class LLBuildProgressTracker: LLBuildBuildSystemDelegate, SwiftCompilerOut
277277
guard command.shouldShowStatus else { return }
278278
guard !self.swiftParsers.keys.contains(command.name) else { return }
279279

280+
let commandName = command.name
281+
let commandDescription = command.description
282+
280283
self.queue.async {
281284
if result == .cancelled {
282285
self.cancelled = true
@@ -286,8 +289,8 @@ final class LLBuildProgressTracker: LLBuildBuildSystemDelegate, SwiftCompilerOut
286289
self.delegate?.buildSystem(self.buildSystem, didFinishCommand: BuildSystemCommand(command))
287290

288291
if !self.logLevel.isVerbose {
289-
let targetName = self.swiftParsers[command.name]?.targetName
290-
self.taskTracker.commandFinished(command, result: result, targetName: targetName)
292+
let targetName = self.swiftParsers[commandName]?.targetName
293+
self.taskTracker.commandFinished(commandDescription, result: result, targetName: targetName)
291294
self.updateProgress()
292295
}
293296
}
@@ -343,12 +346,15 @@ final class LLBuildProgressTracker: LLBuildBuildSystemDelegate, SwiftCompilerOut
343346
// FIXME: This should really happen at the command-level and is just a stopgap measure.
344347
let shouldFilterOutput = !self.logLevel.isVerbose && command.verboseDescription.hasPrefix("codesign ") && result
345348
.result != .failed
349+
350+
let commandName = command.name
351+
346352
self.queue.async {
347-
if let buffer = self.nonSwiftMessageBuffers[command.name], !shouldFilterOutput {
353+
if let buffer = self.nonSwiftMessageBuffers[commandName], !shouldFilterOutput {
348354
self.progressAnimation.clear()
349355
self.outputStream.send(buffer)
350356
self.outputStream.flush()
351-
self.nonSwiftMessageBuffers[command.name] = nil
357+
self.nonSwiftMessageBuffers[commandName] = nil
352358
}
353359
}
354360

@@ -360,13 +366,13 @@ final class LLBuildProgressTracker: LLBuildBuildSystemDelegate, SwiftCompilerOut
360366
// The command failed, so we queue up an asynchronous task to see if we have any error messages from the
361367
// target to provide advice about.
362368
self.queue.async {
363-
guard let target = self.swiftParsers[command.name]?.targetName else { return }
369+
guard let target = self.swiftParsers[commandName]?.targetName else { return }
364370
guard let errorMessages = self.errorMessagesByTarget[target] else { return }
365371
for errorMessage in errorMessages {
366372
// Emit any advice that's provided for each error message.
367373
if let adviceMessage = self.buildExecutionContext.buildErrorAdviceProvider?.provideBuildErrorAdvice(
368374
for: target,
369-
command: command.name,
375+
command: commandName,
370376
message: errorMessage
371377
) {
372378
self.outputStream.send("note: \(adviceMessage)\n")
@@ -530,8 +536,8 @@ private struct CommandTaskTracker {
530536
}
531537
}
532538

533-
mutating func commandFinished(_ command: SPMLLBuild.Command, result: CommandResult, targetName: String?) {
534-
let progressTextValue = self.progressText(of: command, targetName: targetName)
539+
mutating func commandFinished(_ commandDescription: String, result: CommandResult, targetName: String?) {
540+
let progressTextValue = self.progressText(of: commandDescription, targetName: targetName)
535541
self.onTaskProgressUpdateText?(progressTextValue, targetName)
536542

537543
self.latestFinishedText = progressTextValue
@@ -558,22 +564,22 @@ private struct CommandTaskTracker {
558564
}
559565
}
560566

561-
private func progressText(of command: SPMLLBuild.Command, targetName: String?) -> String {
567+
private func progressText(of commandDescription: String, targetName: String?) -> String {
562568
// Transforms descriptions like "Linking ./.build/x86_64-apple-macosx/debug/foo" into "Linking foo".
563-
if let firstSpaceIndex = command.description.firstIndex(of: " "),
564-
let lastDirectorySeparatorIndex = command.description.lastIndex(of: "/")
569+
if let firstSpaceIndex = commandDescription.firstIndex(of: " "),
570+
let lastDirectorySeparatorIndex = commandDescription.lastIndex(of: "/")
565571
{
566-
let action = command.description[..<firstSpaceIndex]
567-
let fileNameStartIndex = command.description.index(after: lastDirectorySeparatorIndex)
568-
let fileName = command.description[fileNameStartIndex...]
572+
let action = commandDescription[..<firstSpaceIndex]
573+
let fileNameStartIndex = commandDescription.index(after: lastDirectorySeparatorIndex)
574+
let fileName = commandDescription[fileNameStartIndex...]
569575

570576
if let targetName {
571577
return "\(action) \(targetName) \(fileName)"
572578
} else {
573579
return "\(action) \(fileName)"
574580
}
575581
} else {
576-
return command.description
582+
return commandDescription
577583
}
578584
}
579585

0 commit comments

Comments
 (0)