Re: Closure iterators and resource management

2020-01-19 Thread spip
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"


Run

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.


Closure iterators and resource management

2020-01-19 Thread spip
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"


Run

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


Run

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


Run

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](https://nim-lang.org/docs/destructors.html) 
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?