简体   繁体   中英

Thread allocates memory, main process dies, what happens?

I am currently checking some code I have for memory leaks and this possibility struck me. Basically the pseudocode of what I'm doing is as follows,

void thread_func()
{
    char *fileName = malloc(someSize);
    /* Do something with fileName and other things */
    /* Enter a critical section */
    modify some global variables
    /*Exit critical section */
    free(fileName);
    return;
}

This function resides inside a DLL. The critical section and other things are initialized by a function which also resides inside the same DLL.

Now, my main process (which is a GUI) has a Cancel button. When the user clicks on that button, I call the DLL's cleanup function which happens to destroy my critical section.

I found that if the user clicks on Cancel during the execution of thread_func() , thread_func() will continue executing. When it reaches the critical section code, the critical section is invalid so I'm exiting right there. This is how I check for the cancel event inside the thread (since nothing else in my application can call the DLL's cleanup during the execution of thread_func() ).

I cannot free fileName in thread_func() when I find that the critical section is invalid. My guess is because thread_func() has lost access to fileName since the main process exited. Is my guess right? My main question is, if I don't free fileName in this case, am I running the risk of a memory leak?

I have searched quite a bit for relevant information and haven't found anything so far. I would be extremely happy if someone can point me in the right direction/answer my question.

Thanks!

EDIT :

I decided to do some preliminary tests based on kol's suggestion (see answer below). I noticed something extremely strange which I just can't understand. Now my code is as follows:

void thread_func()
{
    char *fileName = malloc(someSize);
    /* Do something with fileName and other things */

    if(threadTerminated)
    {
        /* Cleanup */
        return;
    }

    /* Enter a critical section */
    modify some global variables
    /*Exit critical section */
    free(fileName);
    return;
}

And in my GUI, my OnCancel event handler is something like:

void OnCancel()
{
    threadTerminated = TRUE;
    WaitForMultipleObjects(noOfRunningThreads, threadHandles, TRUE, INFINITE);

    /* Other cleanup code */
}

I noticed that WaitForMultipleObjects() hangs indefinitely and my GUI becomes unresponsive. Shouldn't WaitForMultipleObjects() return quickly? Also, none of the cleanup happens in thread_func() if threadTerminated is TRUE .

Here's the strangest part IMO. When I remove WaitForMultipleObjects() , my code works just fine! All the cleanup happens, including the cleanup inside thread_func() . Can someone please help me understand this?

Please note that I'm checking for threadTerminated at only one point for now. I will check it at other important points later. I'm doing this just to see if I've understood what's happening.

Thanks again! Your answers are extremely helpful.

When a process terminates, the OS will free all the memory it's been allocated, so not calling free on the allocated fileName won't cause any problem.

Anyway, I would change the code the following way:

  1. Define a flag which indicates whether the thread should terminate: bool terminated;
  2. When the process is about to terminate, set terminated to true , and wait for the thread to terminate .
  3. In the thread function, check terminated at important points (for example, in the condition check of every loop). If terminated is true , stop everything done by the thread (for example, stop loops), release resources (for example, free memory allocated by the thread), and return.
  4. After the thread terminated (that is, after the thread function returned), the process can release every remaining resource (for example, free memory allocated by the process, delete critical sections etc.) and exit.

This way you can avoid deleting critical sections before threads terminate, and you can free every allocated resource.

  • Your thread should probably have some form of loop to be meaningful.
  • When working with threads, you need to invent some way to gracefully terminate them in a safe, predictable manner.
  • Critical sections are blunt, replace them with a mutex object that a thread can WaitFor.

The proper way to design this would be something like this:

HANDLE h_event_killthread = CreateEvent(...);
HANDLE h_mutex = CreateMutex(...);

...

void thread_func()
{
  const HANDLE h_array [] = 
  { 
    h_event_killthread,
    h_mutex 
  };

  ... // malloc etc

  bool time_to_die = false;

  while(!time_to_die)
  {
    DWORD wait_result;
    wait_result = WaitForMultipleObjects(2,         // wait for 2 handles
                                         h_array,   // in this array
                                         FALSE,     // wait for any handle
                                         INFINITE); // wait forever

    if(wait_result == WAIT_OBJECT_0) // h_event_killthread
    {
      time_to_die = true;
    }
    else if(wait_result == (WAIT_OBJECT_0+1)) //h_mutex
    {
      // we have the mutex
      // modify globals here
      ReleaseMutex(h_mutex);

      // do any other work that needs to be done, if meaningful
    }
  }

  cleanup();
}


// and then in the GUI:

void cancel_button ()
{
  ...
  SetEvent(h_event_killthread);
  WaitForSingleObject(the_thread, INFINITE);
  ...
}

EDIT :

Please have in mind that creating and deleting threads creates a lot of overhead code and may slow down the program. Unless they are worker threads, where the amount of work is significant compared to the overhead, consider to keep threads alive but asleep during the whole lifetime of the program.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM