Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why win32 thread doesn't exit automatically?

Background:

In my application written in C++, I create a worker thread which in turn creates two threads using CreateThread(). The two threads which worker thread creates, talk to WCF Service through a client which is implemented using Windows Web Services API which offers C/C++ application programming interface (API) for building SOAP based web services and clients to them. My application implements only the client using this API.

Problem:

The problem I'm facing is that all other threads exit gracefully, except the worker thread, as you can see yourself, in the image below that WorkerThreadProc uses no CPU cycles yet it doesn't exit. There are also few other threads running which are not created by me, but by the runtime.

The thread states are as follows (as reported by ProcessExplorer):

  • WorkerThreadProc is in Wait:WrUserRequest state.
  • wWinMainCRTStartup is in Wait:UserRequest state.
  • All TpCallbackIndependent are in Wait:WrQueue state.

What are they waiting for? What could be possible causes that I need to look into? Also, what is the difference between WrUserRequest and UserRequest? And what does WrQueue mean? I've absolutely no idea what is going on here.

enter image description here


Here is my WorkerThreadProc code. I've removed all the logging statements except the last one at the bottom of the function:

DWORD WINAPI WorkerThreadProc(PVOID pVoid)
{

    //Initialize GDI+
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;

    Status status = GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    if ( status != Status::Ok )
    {
        return 1;
    }

    GuiThreadData *pGuiData = (GuiThreadData*)pVoid;

    auto patternIdRequestQueue= new PatternIdRequestQueue();
    auto resultQueue = new ResultQueue();

    auto patternManager = new PatternManager(patternIdRequestQueue);
    LocalScheduler *pScheduler = new LocalScheduler(resultQueue, patternManager);

    bool bInitializationDone = pScheduler->Initialize(pGuiData->m_lpCmdLine);
    if ( !bInitializationDone )
    {
        return 0;
    }

    //PatternIdThread
    PatternIdThread patternIdThread(patternIdRequestQueue);
    DWORD dwPatternIdThreadId;
    HANDLE hPatternIdThread = CreateThread(NULL, 0, PatternIdThreadProc, &patternIdThread, 0, &dwPatternIdThreadId);

    ResultPersistence resultPersistence(resultQueue);
    DWORD dwResultPersistenceThreadId;
    HANDLE hResultPersistenceThread = CreateThread(NULL, 0, ResultPersistenceThreadProc, &resultPersistence, 0, &dwResultPersistenceThreadId);

    pScheduler->ScheduleWork(pGuiData->m_hWnd, pGuiData->m_hInstance, ss.str());

    pScheduler->WaitTillDone();
    patternIdThread.Close();
    resultPersistence.Close();

    delete pScheduler; 

    //Uninitialize GDI+
    GdiplusShutdown(gdiplusToken);

    dwRet = WaitForSingleObject(hPatternIdThread, INFINITE);
    CloseHandle(hPatternIdThread);

    dwRet = WaitForSingleObject(hResultPersistenceThread,INFINITE);
    CloseHandle(hResultPersistenceThread);

    SendMessage(pGuiData->m_hWnd, WM_CLOSE, 0, 0);

    //IMPORTANT : this verbose message is getting logged!
    T_VERBOSE(EvtSrcInsightAnalysis, 0, 0, "After sending message to destroy window");

    delete patternManager;
    delete patternIdRequestQueue;
    delete resultQueue;
    return 0;
}

Please see the T_VERBOSE macro, it is used to log verbose message. I see the message is getting logged, yet the thread doesn't exit!


EDIT:

I just commented the following line in my WorkerThreadProc, then worker thread exits gracefully!

SendMessage(pGuiData->m_hWnd, WM_CLOSE, 0, 0);

Does it mean that SendMessage is the culprit? Why would it block the thread the calling thread?

like image 882
Nawaz Avatar asked Feb 10 '12 11:02

Nawaz


1 Answers

If we look at the docs for SendMessage, you can see this little quote:

To send a message and return immediately, use the SendMessageCallback or SendNotifyMessage function. To post a message to a thread's message queue and return immediately, use the PostMessage or PostThreadMessage function.

and this:

Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message. However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed. To prevent this, use SendMessageTimeout with SMTO_BLOCK set. For more information on nonqueued messages, see Nonqueued Messages.

so from this we can see SendMessage will block till the message is processed, which may somehow lead to a deadlock in your code, as the msgproc doesn't reside in your worker thread, leading to a context switch (which is only triggered when the thread's queue is pumped for messages). Try using PostMessage, which immediately returns.

EDIT: there is also a nice little piece of info here on message deadlocks from SendMessage

like image 79
Necrolis Avatar answered Oct 31 '22 14:10

Necrolis