fix: 修复 waitForProcess race condition,添加调试日志

This commit is contained in:
yumoqing 2026-07-01 08:01:40 +08:00
parent acb232d38b
commit ec88e9f530

View File

@ -15,6 +15,21 @@ final class ProgressState: @unchecked Sendable {
} }
} }
/// Thread-safe atomic boolean for one-shot guards
final class AtomicBool: @unchecked Sendable {
private var value = false
private let lock = NSLock()
/// Returns true if this call set it from false to true (first caller wins)
func setTrue() -> Bool {
lock.lock()
defer { lock.unlock() }
guard !value else { return false }
value = true
return true
}
}
public final class FFmpegRecorder: StreamRecorderEngine { public final class FFmpegRecorder: StreamRecorderEngine {
public weak var delegate: StreamRecorderDelegate? public weak var delegate: StreamRecorderDelegate?
public private(set) var isRecording = false public private(set) var isRecording = false
@ -108,6 +123,7 @@ public final class FFmpegRecorder: StreamRecorderEngine {
// cooperative thread // cooperative thread
await waitForProcess(proc) await waitForProcess(proc)
NSLog("[Recorder] ffmpeg process exited, status: %d", proc.terminationStatus)
stderrPipe.fileHandleForReading.readabilityHandler = nil stderrPipe.fileHandleForReading.readabilityHandler = nil
isRecording = false isRecording = false
@ -119,9 +135,11 @@ public final class FFmpegRecorder: StreamRecorderEngine {
throw RecordingError.outputNotFound throw RecordingError.outputNotFound
} }
NSLog("[Recorder] Analyzing output file...")
// //
let ffprobePath = ffmpegPath.replacingOccurrences(of: "ffmpeg", with: "ffprobe") let ffprobePath = ffmpegPath.replacingOccurrences(of: "ffmpeg", with: "ffprobe")
let result = try await analyzeOutput(path: outputPath, ffprobePath: ffprobePath) let result = try await analyzeOutput(path: outputPath, ffprobePath: ffprobePath)
NSLog("[Recorder] Analysis complete, notifying delegate")
delegate?.recorder(self, didFinishWithResult: result) delegate?.recorder(self, didFinishWithResult: result)
} }
@ -141,13 +159,18 @@ public final class FFmpegRecorder: StreamRecorderEngine {
private func waitForProcess(_ proc: Process) async { private func waitForProcess(_ proc: Process) async {
guard proc.isRunning else { return } guard proc.isRunning else { return }
await withCheckedContinuation { (continuation: CheckedContinuation<Void, Never>) in await withCheckedContinuation { (continuation: CheckedContinuation<Void, Never>) in
let resumed = AtomicBool()
proc.terminationHandler = { _ in proc.terminationHandler = { _ in
continuation.resume() if resumed.setTrue() {
continuation.resume()
}
} }
// Race condition: process may have exited between isRunning check and setting handler // Race condition: process may have exited between isRunning check and setting handler
if !proc.isRunning { if !proc.isRunning {
proc.terminationHandler = nil proc.terminationHandler = nil
continuation.resume() if resumed.setTrue() {
continuation.resume()
}
} }
} }
} }