- [ConsoleLogger]: Fix writing large data by chunking + stale handle capture

This commit is contained in:
mahee96
2025-08-06 02:03:18 +05:30
parent 669d33183e
commit 724e8db980

View File

@@ -80,22 +80,55 @@ public class AbstractConsoleLogger<T: OutputStream>: ConsoleLogger{
let shutdownLock = NSLock() let shutdownLock = NSLock()
private func setupReadabilityHandler(for handle: FileHandle?, isError: Bool) { private func setupReadabilityHandler(for handle: FileHandle?, isError: Bool) {
handle?.readabilityHandler = readHandler(for: handle, isError: isError) handle?.readabilityHandler = readHandler(isError: isError)
} }
private func readHandler(for handle: FileHandle?, isError: Bool) -> (FileHandle) -> Void { private func readHandler(isError: Bool) -> (FileHandle) -> Void {
return { [weak self] _ in return { [weak self] _ in
guard let self, let data = handle?.availableData else { return } // Lock first before touching anything
self?.shutdownLock.lock()
defer { self?.shutdownLock.unlock() }
shutdownLock.lock() // Capture strong self *after* lock is acquired
defer { shutdownLock.unlock() } guard let self = self else { return }
let handle = isError ? self.errorHandle : self.outputHandle
guard let data = handle?.availableData else { return }
writeQueue.async { writeQueue.async {
try? self.writeData(data) try? self.writeData(data)
} }
if let fd = isError ? self.originalStderr : self.originalStdout { // 2. Echo to original stdout/stderr if still valid
data.withUnsafeBytes { _ = write(fd, $0.baseAddress, $0.count) } guard let fd = isError ? self.originalStderr : self.originalStdout else {
return
}
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "UnknownApp"
guard fcntl(fd, F_GETFD) != -1 else {
NSLog("[%@] ConsoleLogger: Original FD (%d) is invalid, skipping echo", appName, fd)
return
}
data.withUnsafeBytes { rawBufferPointer in
guard let base = rawBufferPointer.baseAddress else { return }
var remaining = data.count
var offset = 0
let maxChunkSize = 16 * 1024 // 16 KB chunks
// write in chunks, else will throw 'Result too large'
while remaining > 0 {
let chunkSize = min(maxChunkSize, remaining)
let written = write(fd, base.advanced(by: offset), chunkSize)
if written < 0 {
NSLog("[%@] ConsoleLogger: Failed to re-echo to FD %d: %s", appName, fd, strerror(errno))
break
}
remaining -= written
offset += written
}
} }
} }
} }