Search code examples
c++winapithread-safetythreadpoolkernel32

get/check inner kernel32 state for process win32 (for safe usage of TerminateThread )


I've written a threadpool with a terminate option for threads available to the user. As described in Documentation of API terminateThread(),

If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.

I could verify this problem myself: Terminating a thread in that condition caused memory allocation problems (amongst others), but fixing that condition fixed the problems at the same time.

Questions

  1. So, I want to check this inner state after every time terminateThread() has been used. If terminateThread() has caused problem for the inner state of a process in kernel32.dll, I want to raise an exception - and terminate the process after logging to user (unless fixing the inner state is still possible).

    Is this feasible? Maybe by finding the address of the relevant state variable (or anything like that - by matching the pdb file of kernel32 or another way)? The situation is bad for me - if I cannot solve it, I either have to omit the terminate option for the threapool or just leave the thread for itself. Any hints would be appreciated!

  2. Is there any other win32 function that causes similar problems?

  3. a. Is it safe to leave a thread for itself when it has called a blocking kernel32 function that will definitely never return?

    b. What happens if the win32 function returns and the lambda function has been destroyed?

Why am I asking this? (Supplementary information)

I have a custom threadpool in my project where I call some win32 APIs that may sometimes block forever. Hence, I call them using a timeout. When that timeout is reached, I call terminateThread() and have my threadpool return "unsucessfull call state".

Sometimes, my current app reaches a deadlock. I found out that this deadlock is happening in the threadpool, so I'm looking for alternatives to terminateThread() (such as leaving the thread as I described in the question) or trying to fix the inner state, or at least to verify whether terminateThread() is the root of my deadlock.

I'd like to reuse this threadpool in other projects, too, so I should make it safe.

Update: Problem fixed.

I found the bug in my app: It was actually a call to terminateThread() when the timeout in my threadpool was already low (about 200 ms). The thread was killed in a moment when it wasn't blocking (i.e., if a longer timeout period had remained, it would have worked and returned correctly). From the kernel stack trace I found out that in kernel mode, a mutex was being locked while the thread was terminated, and while the thread was exiting, other threads were already waiting for that mutex.

The problem first appeared to go away by increasing the minimum timeout to 1000 ms, but I wasn't content with that: My solution was to create lambda on heap when timeout reached, to leave the lambda and the thread for itself without terminating, and to add it to a list of _ToTerminateThreads. The list gets terminated once in 10 minutes (waits 10 min., copies the list, waits another minute, and then terminates the threads and deletes the lambdas).

Still, after testing and hours of debugging I was getting heap corruptions. Finally I found out the following: The threads that I had left for deletion wrote to the memory that had been used by the user function (which was passed to threadpool) - and they were freed because threadpool had returned. This had caused the ultimate problems, so the final solution was to increase the timeout to a safe amount.

I recommend to everyone who needs such a feature to deploy it to a child process, and to terminate that process instead of using a thread.

I keep this question open because the main 4 questions haven't been answered yet. For my problem, I don't need their answer anymore, but they may be interesting for other members of stackoverflow.


Solution

  • My issue resolved although it has nothing to do with 3 questions in post. I try to answer them in reverse order:

    • ad 3.b.) If an external function returns and your local lambda has been deleted, the cpu won't know this and will try to process the bytes at that offset as CPU instructions. This surely will mess you up, so never do that!

    • ad 3.a.) Yes, it is safe to leave if you're 100% sure the external function will never return (otherwise it'll mess your app when return

      1. if you deleted the rest of the code with the same way explained in b.
      2. if you haven't deleted the lambda or it's a gobal function, it'll run the rest of function which may be editing dynamic allocated memories (heap, not stack) which has been deallocated and cause heap corruption or simply just edit some global variables).
    • ad 2.) I googled for dangerous winapi functions and didn't find any result other than TerminateThread(). If you know about one, please add a comment or write another answer.

    • ad 1.) I don't have any solution for checking/fixing the inner kernel32 state for the process that Microsoft refers to. I think a guy at Microsoft who has read kernel32.dll source code should answer this.

    Apart from this kernel32 state, TerminateThread() will cause lots of other problems (like resource/heap allocations, mutex locks, leaks and so on) so never use it unless you 100% sure what you are doing.

    Read the article @RichardCritten linked in the comments: TerminateThread()

    What was the bug in my code?

    I was calling TerminateThread() with a low timeout (300 ms). Randomly when a machine had low resources, the function was still operating (I mean non-blocking call!). I terminated that function and thereby caused a kernel mutex to be locked. This locked mutex made all other threads wait - and not exit when they returned.

    Remarks

    I answered my own question from what I found after I didn't receive any answer. Hence, it may contain some wrong information. Please correct me if anything is wrong in this.