How can a closure iterator manage resources? In my particular case, I have a closure iterator that create channels and threads. When the iterator is aborted from the caller side, how can I free the resources I have allocated in the iterator?
For instance, consider the following example:
import strutils
import os
proc stream(m: int): iterator: int =
iterator iter: int {.closure.} =
var i = 0
echo "Allocate resources"
while true:
yield i
inc(i)
if i == m:
echo "Iterator max reached; end of iterator"
break
echo "Deallocate resources"
result = iter
proc main =
let iter = stream(parseInt(paramStr(1)))
for i in iter():
echo "i=", i
if i == 10:
echo "Aborting iteration in main after 10 items..."
sleep(2_000)
break
echo "Out of iterator in main"
echo "Before main"
main()
echo "After main"
This code creates an iterator that count up to a maximum specified from the command line, but after 10 loops the caller procedure aborts the iterator.
If we run it like ./pug 5, we see that the iterator allocates the resources and frees them when it naturally completes.
Before main
Allocate resources
i=0
i=1
i=2
i=3
i=4
Iterator max reached; end of iterator
Deallocate resources
Out of iterator in main
After main
But if we run it like ./pug 15, we see that the deallocation does not happen:
Before main
Allocate resources
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
i=10
Aborting iteration in main after 10 items...
Out of iterator in main
After main
Enclosing the iterator code with a try: ... finally: ... does not help because there are no exceptions in the game. I've looked at the Nim Destructors and Move Semantics documentation but that does not seem to apply there either.
So, how do I ensure that resources are freed when a closure iterator is out of scope?
Just to add that I understand that the iterator can be resumed, so perhaps freeing resources must not be part of the code of the iterator, though it should access variables declared in the iterator...
proc main =
let iter = stream(parseInt(paramStr(1)))
for i in iter():
echo "i=", i
if i == 10:
echo "Aborting iteration in main after 10 items..."
sleep(2_000)
break
for i in iter():
echo "i2=", i
echo "Out of iterator in main"
But at least it should be called when the iterator variable becomes out of scope and that's the reason why I thought I could find something in Nim Destructors and Move Semantics or compiling with -gc:arc but I was unable to get deallocation of resources.