Fix a memory leak in DispatchData.withUnsafeBytes
by ahoppen · Pull Request #850 · swiftlang/swift-corelibs-libdispatch (original) (raw)
DispatchData.withUnsafeBytes
created a new dispatch_data_t
by calling dispatch_data_create_map
. I assume that the intention was that this memory was freed when data
is destroyed, based on the presence of _fixLifetime(data)
but data
was just a plain dispatch_data_t
C struct, that doesn’t have any cleanup operations associated with it when destroyed.
To fix the leak, wrap the dispatch_data_t
in a DispatchData
, which takes over the ownership of the dispatch_data_t
and releases it when data
gets destroyed.
Alternatively, _fixLifetime
could have been replaced by _swift_dispatch_release(unsafeBitCast(data, to: dispatch_object_t.self))
but I think using DispatchData
is the cleaner solution.
For future reference, a minimal reproducer for this issue was
import Dispatch import Foundation
func run() { let queue = DispatchQueue(label: "jsonrpc-queue", qos: .userInitiated)
let receiveIO = DispatchIO( type: .stream, fileDescriptor: FileHandle.standardInput.fileDescriptor, queue: queue, cleanupHandler: { _ in } )
receiveIO.setLimit(lowWater: 1) receiveIO.setLimit(highWater: Int.max)
receiveIO.read(offset: 0, length: Int.max, queue: queue) { done, data, errorCode in guard let data, !data.isEmpty else { return }
data.withUnsafeBytes { (pointer: UnsafePointer<UInt8>) in }
} }
try await Task.sleep(for: .seconds(2)) print("start") run() try await Task.sleep(for: .seconds(10))
Then run it as
seq 1 100000000 | .build/debug/repro
and attach heaptrack
to it during the initial 2s waiting period by running
heaptrack --pid -$(pidof repro)
Heaptrack will list multiple GB worth of leaks without this fix.