I have noticed a situation where a failed force-unwrapping, try!
, or fatalError
in the LLDB REPL causes memory leaks.
I'm very interested in finding a way to make them not leak memory, because force-unwrapping, try!
and fatalError
are very convenient to use in the REPL when you're just playing around and you don't want to write code that handles errors with Swift's error handling mechanisms.
The leak
Consider what happens if you run this in the LLDB REPL:
func eatMyMemory(_ thing: Int?) {
let veryBigArray = Array(repeating: Int64(1), count: 100_000_000)
thing!
}
eatMyMemory(nil)
This leaks memory, because the optional unwrapping halts execution before the cleanup code that releases veryBigArray
runs. Nothing that you can do (as far as I know) will make LLDB run the cleanup code.
Fix idea
I'm curious what people think of this fix idea: Teach the compiler to generate cleanup code for each function that makes it possible to cleanly exit that function. (This is kind of like the cleanup tables that some C++ implementations use to handle exceptions). Whenever the LLDB REPL unwinds the stack after a fatal error, it can run the appropriate cleanup codes.
This sounds pretty big and complicated. At least it can be completely turned off for code that is not intended to run in the REPL, so it won't add any cost to non-REPL code.
Are there any simpler ways to fix this? Any preexisting code that does something similar that I could use?
More context: Swift for TensorFlow
I noticed this situation while I was thinking about Swift for TensorFlow. In TensorFlow, it's common to run stuff like this:
var model = Model()
for epoch in 0..<10 {
let update = bigComputationThatAllocatesLotsOfIntermediateStuff()
model += update
}
Now there are a few ways that the leak can be triggered:
-
bigComputationThatAllocatesLotsOfIntermediateStuff
often encounters runtime errors (e.g. because the user tries to multiply matrices with incompatible shapes). In our TensorFlow library, these runtime errors are implemented asfatalError
because it would be very onerous to make every mathematical operationthrows
. So the leak occurs. - The user might want to halt execution and unwind the stack because something is taking longer than they expected. This also causes the leak.