defer for cleanup
A procedure with three returns after its allocate needs three
deallocates, and the fourth return someone adds later leaks. defer
writes the release once, on the line after the allocation, and runs it on
every way out of the block. The statement itself is introduced in
defer; this page shows it paired with memory.
'' deallocate, and say so, so the program shows when it happens.
sub Release( byval p as any ptr )
deallocate( p )
print " freed"
end sub
'' The squares 1, 4, 9 ... of the first n numbers, summed through a
'' scratch buffer. Every way out after the allocate frees it.
function SumOfSquares( byval n as int32 ) as int64
if n < 1 then return 0 '' nothing allocated yet
dim as int64 ptr squares = allocate( n * sizeof( int64 ) )
defer Release( squares ) '' written once, beside it
if squares = nullptr then return -1 '' freeing null is harmless
for i as int32 = 0 to n - 1
squares[i] = ( i + 1 ) * ( i + 1 )
next
if n > 1000 then return -2 '' another way out
dim as int64 total = 0
for i as int32 = 0 to n - 1
total += squares[i]
next
return total
end function
dim sizes( 0 to 2 ) as int32 = { 0, 3, 2000 }
for each n in sizes
print "n ="; n
var sum = SumOfSquares( n )
print " result"; sum
next
Output
n = 0
result 0
n = 3
freed
result 14
n = 2000
freed
result-2
Release frees the block and prints freed, so the output shows when the
deferred call runs.
- With
n = 0the function returns before theallocate, and before thedeferline is reached, so nothing is freed: adeferonly takes effect once the program has passed it. - With
n = 3the function reaches its lastreturn, and the block is freed on the way out. - With
n = 2000it leaves early throughreturn -2, and the block is freed all the same.
Three habits make this reliable:
- Write the
deferon the line after theallocate, before the check for null.deallocate( nullptr )does nothing, so the failure path is covered too, and the pair cannot drift apart. - The deferred statement reads its variables when it runs, at the end of
the block, not when the
deferline is reached. If areallocatestores a new address insquares, the right block is still freed. - Allocate and defer in the procedure's own body, not inside an
if. Adeferruns at the end of the block it is written in, so inside anifit frees the memory atend if.
defer suits a resource used in one procedure. When the same kind of
resource is acquired in many places, a type with a destructor
puts the release where it cannot be forgotten.
Activities
- Add a second scratch buffer to
SumOfSquareswith its owndefer. In which order are the two freed? - Move the
allocateand itsdeferinside anif n > 0 then ... end ifblock. Where doesfreedappear now?