Looking at the log you provided in the GitHub issue, the internal per-thread 
dispatcher (which is a `ref` object) is what causes the leak, as `.threadvar`'s 
aren't destroyed on thread exit.

* * *

Not directly related to the leak, but the way you're using `cleanupThread` 
doesn't guarantee that the list with potential cycle roots is empty on thread 
termination. Consider the following:
    
    
    type Cycle = ref object
      x: Cycle
    
    proc run() {.thread.} =
      var x = Cycle()
      x.x = x # create a cycle
      discard x # make sure `x` is not moved
      cleanupThread()
    
    
    Run

In the case shown above, the cycle collector is run _before_ `x` goes out of 
scope. When `x` goes out of scope, the referenced cell will be registered as a 
potential cycle root (which allocates memory for ORC's root list), and you'll 
thus get a memory leak.

To make sure that `cleanupThread` is called _after_ the thread's procedure 
exits but _before_ thread destruction, you can use 
`system.onThreadDestruction`, like so:
    
    
    proc run() {.thread.} =
      onThreadDestruction(cleanupThread)
      # `cleanupThread` is invoked even if `run` exits due to an exception or 
early return
      ...
    
    
    Run

Of course, it'd be better if `Thread` cleans up after itself instead.

Reply via email to