Adi Levin's Blog for programmers

September 19, 2009

Ending a thread

Filed under: Multithreading — Adi Levin @ 1:59 pm
Tags: , , ,

There are three ways to end a thread: Return from the thread function, call ExitThread from within the thread, and call TerminateThreadfrom any thread. The best thing to do is return from the thread function. This way, you guarantee that all destructors of automatic variables have been called, and that thread termination is done properly.

TerminateThread – never use it!

BOOL WINAPI TerminateThread(
  __inout  HANDLE hThread,
  __in     DWORD dwExitCode
);

Some programmers make the mistake of waiting with a timeout until a thread is finished, and then kill it using TerminateThread in case of timeout. Don’t do that! TerminateThread terminates the thread immediately without doing the proper clean-up and notifications. It doesn’t free the stack (1Mb leak), it doesn’t free owned critical sections, it doesn’t call the DLL entry functions to notify them that the thread is being detached. Of course, it doesn’t give the thread a chance to free objects allocated on the heap.

The result is a potential deadlock, and the worst thing is that it happens with low probability, so there is a good chance that software testing will not detect it, and your customer will be the first to encounter it. Why a potential deadlock? Because certain library functions (such as malloc, free and MFC functions), use critical sections to synchronize access of threads within the process to certain resources (such as the heap). This means that whenever you call malloc you are waiting for a critical section to be released. But if you called TerminateThread while that thread owned that critical section, the wait will result in a dead-lock.

In fact, there is no justification to using TerminateThread. It is only safe to use when you control exactly what that thread is doing (e.g. you know that it doesn’t do heap allocation, and it is not inside a critical section). But if you are the programmer of that thread’s function, then why not write code that returns safely from the thread function upon some event or flag, or calls ExitThread when needed?

ExitThread – use it with care!

ExitThread can only be called from within the thread which you want to exit. It frees the stack and calls the entry point of each DLL in the process to notify it that the thread is being detached. However, it does not perform destruction of C++ objects as done when returning from a function. This is why you have to use it with care.

In certain situations, it make sense to use ExitThread for the purpose of simplifying the code. For example, if the thread is in alertable state (i.e. waits for APC functions), other threads can queue an APC function that calls ExitThread, thereby causing the thread to exit.

The best way: Returning from the thread function

The best way to write a thread function is to design it so that another thread (the main thread, or the thread that is managing the worker threads), can cause it to end in an orderly fashion.

 For example, if you run a loop with many iterations, check the value of a certain flag in each iteration, and return if it is non-zero. Other threads can change the value of that flag when they want your thread to finish.

 

If your thread waits for some object to be signaled, using WaitForSingleObject(obj,…), and you want to be able to stop the wait from another thread, replace the wait function by WaitForMultipleObjects, and wait for obj and for an event that can be signaled by another thread, when termination is needed.

More generally, in any point in the code where you want to be able to interrupt the operation of the thread and cause it to exit, you should place the appropriate code.

Advertisements

4 Comments »

  1. […] It is important to note that terminating a thread is generally a terrible idea and should not be carried out in the vast majority of cases. It is usually the wrong thing to do. […]

    Pingback by Thread Termination and Freeing the User Mode Thread Stack | Nick's Tech Blog — January 5, 2012 @ 5:54 pm | Reply

    • Exactly. This is the point I’m trying to make. But I’m also trying to explain exactly WHY thread termination is a bad idea 🙂

      Comment by adilevin — January 5, 2012 @ 5:57 pm | Reply

  2. I am writing a program for Windows (2008 Server, 64 bit)
    And I have to use GetFileInformationByHandleEx() function. Sometimes the function hangs forever. So I decided move it to a thread. If the thread take more than 0.1 sec it is terminated (via TerminateThread function). Of cause, I have memory leak.
    In general the program looks as following:
    while ( 1 )
    {

    ThreadHandle = CreateThread( 0, 0, ThreadProc, &ThreadParams, 0, 0 );
    if( WAIT_TIMEOUT == WaitForSingleObject( ThreadParams.hFinishedEvent, 100 ))
    {
    // The thread is considered as hung
    TerminateThread( ThreadHandle, 0 );
    }

    }

    DWORD WINAPI ThreadProc( LPVOID lParam )
    {

    BOOL bRet = GetFileInformationByHandleEx(…); // sometimes it hangs forever

    SetEvent( pThreadParam->hFinishedEvent );
    return 0;
    }

    Is it possible to avoid memory leak?

    PS I used GetFinalPathNameByHandle() function instead of GetFileInformationByHandleEx(). But it hangs too, so it didn’t help.

    Comment by Denis — January 24, 2014 @ 3:01 pm | Reply

    • Hmmm… I don’t know GetFileInformationByHandle very well. But if there’s no way to prevent it from hanging, then maybe you should consider moving it to a different process? If it’s a different process then it’s perfectly ok to terminate it violently, and it won’t have any bad effect on your process at all. Of course the downside of this is that you’ll have to figure out a way to transfer data between the two processes, which can be simple or complicated depending on the amount of data you need to transfer and the performance requirements.

      Comment by Adi Levin — January 24, 2014 @ 6:56 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: