none
How to tell Concurrency Runtime to "clean after itself"?

    Question

  • Consider the following scenario: a DLL in a process uses Concurrency runtime to do some work. It uses external library which actively uses thread-local storage to store per-thread data. The actual pointer to the data is stored in thread TLS slot. The library handles DLL_THREAD_DETACH in its DllMain and frees the memory it allocated for each thread.

    Now the question is: how can I instruct the Concurrency runtime to stop threads it created at a specific point in time? Because currently it seems that the threads created "for a library" continue running even after I unload DLL from the address space.

    I've tried to play with non-default scheduler - the DLL creates a scheduler for itself and shuts it down before unloading. This does not seem to stop the threads, though. The event I registered with a call to RegisterShutdownEvent is signaled, but it only seems to trigger the threads stop (that is, stopping continues in parallel). Anyway, according to the documentation, it only guarantees you that no tasks are executing and says nothing about threads.

    Is it possible to control the lifetime of threads the Concurrency Runtime manages? If not, what is a recommended scenario for such use-case? How can I clean up the memory DLL allocated for threads created by Concurrency Runtime?

     

    Tuesday, August 9, 2011 3:43 PM

Answers

  • Hi Barfy,

    In general, as with any threadpool that supports a task model, developers are not going to be able to rely on or control the underlying threading mechanisms. The runtimes / threadpools gain efficiency by reusing threads when running tasks. Hence, direct use of TLS without explicitly knowing when to clean up would not be recommended.

    Two (not so great) recommendations specific to your problem above:

    1. Make a copy of the pointers to the heap stored in each TLS. In dll detach, simply go through the list and free up the memory.

    2. As you noted, it is not a great idea to wait on scheduler shutdown as a way to know that all threads are indeed destroyed. In every thread that you use TLS, you could GetCurrentThread, and store the handle of each in an array. In DLL detach, use WaitForSingleObject over each of thread handle.

     

    Thanks

    Rahul

    Wednesday, September 28, 2011 1:19 AM
    Owner

All replies

  • Barfy,

    You may consider using Concurrency::combinable<T> instead of TLS. The intention of combinable was to enable accumulation type of scenarios to work in a more performant manner, but it could be used when you want thread local copies of certain data. combinable.local() gives you access to the thread local state, and you can supply a function to intialize it when combinable.local() is called for the first time on a thread. Also, when you destruct a combinable object, it destructs all copies of its state. If you're storing raw pointers, you may want to use auto_ptr so that your memory is cleaned up. Deleting the combinable object when you can be assured that none of your tasks are running (and thereby accessing the data) is sufficient.

    Here's a link to the msdn documentation for combinable http://msdn.microsoft.com/en-us/library/dd492850.aspx

    The workaround Ameen refers to will only work if your scheduler is the only scheduler in the process.

    --Geni

    Wednesday, August 10, 2011 5:09 PM
  • Unfortunately, the Concurrency::combinable is not a solution here, because, as I said in my first post, I'm using the external library (namely, boost::wave, which in turn uses boost::thread and which creates its own TLS slot and stores a pointer to heap-allocated object in it).

    Another problem is that I don't see a post Ameen has submitted - I have only received it by e-mail and don't see in this thread (this is seen on two computers, so this is not a local problem).

    Nevertheless, it does not work even if I have a single scheduler and detach it before unloading a DLL. If the workaround includes sleeping to give time to stop threads, I don't consider this a good workaround.

    A topic Ameen referred me to also describes the similar problem and I'm glad that Microsoft considers that to be a bug and plans to work around it in future releases of the library. This issue with DLL and memory-leak reporting are very important and a library should provide enough mechanisms to handle them.

    My current workaround includes patching boost::thread to build a chain of thread-local records it allocates and then provide a function to delete them all at once.

     

    Thursday, August 11, 2011 11:22 AM
  • Hi Barfy,

    In general, as with any threadpool that supports a task model, developers are not going to be able to rely on or control the underlying threading mechanisms. The runtimes / threadpools gain efficiency by reusing threads when running tasks. Hence, direct use of TLS without explicitly knowing when to clean up would not be recommended.

    Two (not so great) recommendations specific to your problem above:

    1. Make a copy of the pointers to the heap stored in each TLS. In dll detach, simply go through the list and free up the memory.

    2. As you noted, it is not a great idea to wait on scheduler shutdown as a way to know that all threads are indeed destroyed. In every thread that you use TLS, you could GetCurrentThread, and store the handle of each in an array. In DLL detach, use WaitForSingleObject over each of thread handle.

     

    Thanks

    Rahul

    Wednesday, September 28, 2011 1:19 AM
    Owner