Re: MFC DLL - ExitInstance hang on WaitForSingleObject
On Fri, 1 May 2009 16:31:01 -0700, Erakis
<Erakis@discussions.microsoft.com> wrote:
Hi,
Hi! Jump directly to the end to see what I think the problem is. Start from
the beginning for some more or less unrelated tips.
Here is a simple pooling class implemented into a MFC DLL project.
------------------------------------------------------------------------
BOOL CPooler::StartPooling()
{
// Create startup event
m_hStartupEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
// Create main thread
m_hMainThread = CreateThread( NULL, 0, MainThreadProc,
static_cast<LPVOID>( this ), 0, &g_dwThreadID);
// Check for startup event
switch( WaitForSingleObject( m_hStartupEvent, 1000) )
{
case WAIT_TIMEOUT :
bRet = FALSE; // Thread time-out
break;
case WAIT_OBJECT_0 :
bRet = TRUE; // Thread sucessfully started
break;
}
// Close startup handle
CloseHandle(m_hStartupEvent);
m_hStartupEvent = NULL;
return bRet;
}
You should use AfxBeginThread and CWinThread in an MFC program. See this
page for info on using CWinThread correctly:
http://members.cox.net/doug_web/threads.htm
The logic in StartPooling is rather creative.
BOOL CPooler::StopPooling()
{
// Singal to main thread to stop
SetEvent(m_hEventKill);
// Check thread state
if (WaitForSingleObject( m_hMainThread, 10000) == WAIT_TIMEOUT)
{
// If thread is still running then stop it brutally
if (m_hMainThread != NULL)
{
TerminateThread( m_hMainThread, 0 );
}
}
// Thread is null
CloseHandle( m_hMainThread );
m_hMainThread = NULL;
return TRUE;
}
That's even more questionable. If the thread is still running after being
asked to exit, it has a bug, and terminating the thread isn't going to help
you find the bug. It may also lead your program to subsequently malfunction
in seemingly unrelated ways.
DWORD WINAPI CPooler::MainThreadProc(LPVOID lpParameter)
{
// Get pointer on CModBusTcpPooler class object
CPooler *pThis = reinterpret_cast< CPooler *>( lpParameter ) ;
// Cretae kill event (for main thread)
pThis->m_hEventKill = CreateEvent(NULL, FALSE, FALSE, NULL);
// Signal startup event
SetEvent( pThis->m_hStartupEvent );
// Loop until kill event has not been signaled
UINT32 un32Index = 0;
while(WaitForSingleObject( pThis->m_hEventKill, 5) != WAIT_OBJECT_0)
{
// Do pooler job
// Give time to CPU
Sleep(5);
}
You're doing a timed wait at the top of the loop, so you don't need to
sleep here. It probably doesn't make any sense to sleep anyway given the
preemptive scheduler.
// No need this handle anymore
CloseHandle(pThis->m_hEventKill);
pThis->m_hEventKill = NULL;
return FALSE;
}
CPooler::~CPooler(void)
{
// Stop main thread
StopPooling();
}
------------------------------------------------------------------------
Now, always in this DLL
That's likely your problem...
I have implemented a wrapper containing an
array of 100 available CPooler that can be created, started, stopped
and removed. Here is the more important part.
------------------------------------------------------------------------
CPooler* g_Pooler[100];
extern "C" POOLER_LIB_API INT32 __cdecl PoolerCreate(...)
{
// Look for empty place in the array and create the Pooler
int i = 0;
while( i < MAX_POOLER && g_Pooler[i] != NULL ) ++i;
// If empty space was found
CPooler* pTcpPooler = NULL;
if (i < MAX_POOLER)
{
// Create new pooler
pPooler = new CPooler(...);
// Start it !
if ( pPooler->StartPooling() )
{
g_Pooler[in32Result = i] = pPooler;
}
else
{
// Cannot start so delete instance and return error
delete pPooler;
in32Result = -2;
}
}
else
{
// No more space to create a pooler
in32Result = -1;
}
return in32Result;
}
------------------------------------------------------------------------
Now simply to test the class, inside the InitInstance function, I created
a new Pooler and then I delete it just after. Like this
------------------------------------------------------------------------
BOOL CPoolerLibraryApp::InitInstance()
{
CWinApp::InitInstance();
CPooler* p = new CPooler();
p->StartPooling()
delete p; // PROBLEM HERE !
return TRUE;
}
------------------------------------------------------------------------
But I got some very strange behavior while the delete operation and after !
Like the heap was corrupted or I don't know. Anyway the application is
freezing
on this line :
if (WaitForSingleObject( m_hMainThread, 10000) == WAIT_TIMEOUT) inside the
function CPooler::StopPooling().
I put a brake point
That spelling makes sense, but it's really "breakpoint".
on the "return FALSE;" line of the function
CPooler::MainThreadProc. While the destructor is called the thread is well
stopped
but the WaitForSingleObject still wait for thread ending. WHY ?
I already try to create a Pooler inside the InitInstance and destroying it
inside
the ExitInstance and I get the same result.
PS : To call the function of this DLL I made a simple MFC dialog project,
use LoadLibray, GetProcAddress, etc...
Any help should be VERY appreciated because I worked on this problem
while 16 hours :(
IIRC, InitInstance and ExitInstance for MFC DLLs are called in DllMain
context, from which it is forbidden to do things like create threads. This
is definitely true for globals, whose ctors and dtors are called from
DllMain. Therefore, you need to initialization and destroy these objects
from EXE context.
--
Doug Harrison
Visual C++ MVP