-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closure iterators can leak resources and there is no easy way to prevent it #13289
Comments
I'd recommend that you put your code in a |
Yes. This example is not about memory leaks from variables but leaks from resources. Valgrind is used only to make visible that the file can't be closed when the iterator does not finish, with memory leaking as a side effect. With variables global or local to a main procedure, the result is the same. |
Here is the way to solve this type of problem as hinted here and demonstrated by @Yardanico in #15394. One has to encapsulate the resource in an import os
type
AutoClose = object
file: File
proc `=destroy`(ac: var AutoClose) =
echo "Destroying AutoClose"
ac.file.close()
echo "File closed"
proc bar(name: string): iterator: string =
iterator foo: string {.closure.} =
echo "Open file ", name
var ac = AutoClose(file: open(name))
var line: string
while readLine(ac.file, line):
yield line
echo "Close file ", name
close(ac.file)
result = foo
echo "Program starts"
var iter = bar(paramStr(1))
var i = 0
for l in iter():
inc(i)
echo "Read line #", i, "=", l
if i > 5:
break;
echo "Program completed" When executed, on can see that the file resource is released and correctly closed. ./bug bug.nim
Program starts
Open file bug.nim
Destroying AutoClose
File closed
Read line #1=import os
Read line #2=
Read line #3=type
Read line #4= AutoClose = object
Read line #5= file: File
Read line #6=
Program completed
Destroying AutoClose
File closed Running valgrind shows that everything is fine now $ valgrind ./bug bug.nim
==9683== Memcheck, a memory error detector
==9683== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9683== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==9683== Command: ./bug bug.nim
==9683==
Program starts
Open file bug.nim
Destroying AutoClose
File closed
Read line #1=import os
Read line #2=
Read line #3=type
Read line #4= AutoClose = object
Read line #5= file: File
Read line #6=
Program completed
Destroying AutoClose
File closed
==9683==
==9683== HEAP SUMMARY:
==9683== in use at exit: 0 bytes in 0 blocks
==9683== total heap usage: 3 allocs, 3 frees, 5,592 bytes allocated
==9683==
==9683== All heap blocks were freed -- no leaks are possible
==9683==
==9683== For lists of detected and suppressed errors, rerun with: -s
==9683== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) |
Seems to me this ought to work with |
@jlokier, this forum thread explains the real behaviour of |
A closure iterator can leak memory or other critical resource and there is no easy way to prevent it. In the following example, the resource is a file, but it could be a system lock or a database cursor or shared memory.
Example
Current Output
Compile with
nim c -d:useMalloc --gc:arc c.nim
.Calling it with a text file containing more than 5 lines with valgrind using its source code as parameter:
There is no way to close the file used by the iterator when it is aborted.
Expected Output
When called with a file with less than 5 lines, the iterator completes and the file resource is released.
Possible Solution
object
in the closure iterator and define this object type destructor=destroy
, managing the iterator status (completed or aborted) and a reference to the resource, to be called back when the iterator variable is destroyed, and then release the resource.addQuitProc
, but this limits the number such iterators used in the program...Additional Information
--gc:arc
but the same problem exists with the default GC.try: ... finally: ...
is of no use as there is no exception involved.for ... else: ...
to deal with this type of problem. I think the best way to deal with this problem would be to have a new language construct to deal with context/resource management, as resource management would be local (lease/release in the same part of the source code), giving less opportunity for bugs.The text was updated successfully, but these errors were encountered: