Click to See Complete Forum and Search --> : Problem with Multithread DLL:


sgonepudi
February 28th, 2006, 01:24 PM
I have created a Multithread DLL. I used the following design for creating multithread DLL.

1) Created Queue based Thread Pool class
2) Created a User class (Ex: CPoolItem) which contains the some logic.
3) Exported one function for creating Thread Pool (Ex: CreateThreadPool).
4) Exported one function for deleting Thread Pool (Ex: DeleteThreadPool).
5) Exported one function. it takes 2 numbers and returns the result.



extern "C" long __stdcall Add(int a,int b)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
HMODULE hMod = ::GetModuleHandle( "Test.dll" );
ASSERT( hMod );
AfxSetResourceHandle( hMod );
long LResult = 0L;

CPoolItem *pPoolItme = new CPoolItem(a,b); //User class

theApp.m_Pool.AddClientReqObj(pPoolItme);//Inserting the request to the Thread Pool queue.

if(theApp.m_Pool.ProcessClientRequest(pPoolItme)) // Waiting for the completion of Client Request
LResult = pPoolItme->rtnResultValue(); //Getting the result

delete pPoolItme;//Deleting the object
return LResult;// returning the result
}



I want to test the above dll with multiple requests at a time, for that I created a Dialog Based Application. In that application I have loaded the above DLL and created the Thread Pool (DLL Thread Pool) in OnInitDailog() function and deleted the Thread Pool and Unloaded the DLL in OnClose() function.

I have placed one button in the Dialog box. In the button click event, created 50 threads and each thread calls the Add function (which is present in the DLL) with different data.

Everything is working fine. I clicked the button continuously for testing. There is no problem.

Here I have some questions:
1) Is this right design for creating MultiThread DLL?
2) In this design ThreadPoc takes the PoolItem object from the Queue and it calls the Add member function of PoolItem.

I have created CDatabase object, open the object and just closed the object in Add member function of PoolItem class.

At this time my DLL is giving following exception.

HEAP: Free Heap block 128eee0 modified at 128ef08 after it was freed

But this exception is coming some times only. It is coming in the below statement.

CPoolItem *pPoolItme = new CPoolItem(a,b);

3) I have created CDatabase object as a member variable. I opened and closed the database connection in the Add member function. In this situation OpenEx function is not allowing to connect the database.

It is giving Access Violation Exception.

If any body knows about this please let me know the reason.

Arjay
February 28th, 2006, 02:53 PM
What method are you using to synchronize access to the pool? Critical Section? Mutex?

Arjay

sgonepudi
February 28th, 2006, 03:01 PM
I used Critical Section for Pool synchronize

Arjay
February 28th, 2006, 05:59 PM
I used Critical Section for Pool synchronizeOkay,

Are you synchronizing the following calls?

theApp.m_Pool.AddClientReqObj(pPoolItme
if(theApp.m_Pool.ProcessClientRequest(pPoolItme))
LResult = pPoolItme->rtnResultValue();

Could you show the code (or at least the synchronization portions)?

Arjay

sgonepudi
March 1st, 2006, 08:27 AM
AddClientReqObj Function:


bool CThreadPool::AddClientReqObj(CPoolItem *pBValidation)
{
if(pBValidation == NULL)
{
TRACE0("Invalid Validation Object");
return false;
}
else
{
EnterCriticalSection(&m_CriticalSection);

//insert the item in to queue
m_pValidationQueue->push(pBValidation);

//Unlock
LeaveCriticalSection(&m_CriticalSection);
//signal semaphore
if (!ReleaseSemaphore(m_phSincObjectsArray[SEMAPHORE_INDEX],1,NULL))
{
assert(false);
return false;
}
return true;
}
}


ProcessClientRequest function:

bool CThreadPool::ProcessClientRequest(CPoolItem* pBValidation)
{
bool result=false;

if(pBValidation == NULL)
{
TRACE0("Invalid Validation Object");
return false;
}
else
{
DWORD dwWaitSignal;
//Wait for valdiation process completion
dwWaitSignal = ::WaitForSingleObject(pBValidation->hWorkComplete,INFINITE);

//loc
EnterCriticalSection(&m_CriticalSection);

switch(dwWaitSignal)
{
case WAIT_OBJECT_0:
result = true;
}

//release lock
LeaveCriticalSection(&m_CriticalSection);
}
return result;
}


Thread Handling Procedure:

unsigned long __stdcall CThreadPool::ThreadFunc( void* pParam )
{
CThreadPool* pPool = (CThreadPool*)pParam;

CPoolItem* pPoolItem = NULL;

DWORD dwWaitResult;

for(;;)
{
//wait for one of the two Events
// 1. Shutdown Event -
dwWaitResult = WaitForMultipleObjects(NUMBER_OF_SYNC_OBJ,pPool->m_phSincObjectsArray,FALSE,INFINITE);

switch(dwWaitResult - WAIT_OBJECT_0)
{
case SHUTDOWN_EVENT_INDEX:
return 0;
case SEMAPHORE_INDEX:
//take the alerted first pool item from queue
pPoolItem = pPool->RemoveObject();

if(pPoolItem == NULL)
{
assert(false);
break;
}

//do all that needed with the pool item
pPoolItem->DoWork();
break;
};
}
return 1;
}


RemoveObject code:

CPoolItem* CThreadPool::RemoveObject(void)
{

CPoolItem* pValidationObj;


EnterCriticalSection(&m_CriticalSection);

pValidationObj = m_pValidationQueue->front();

m_pValidationQueue->pop();

LeaveCriticalSection(&m_CriticalSection);


assert(pValidationObj != NULL);


return pValidationObj;
}

Arjay
March 1st, 2006, 12:11 PM
The Q synchronization looks okay. Are you able to declare a CDatabase object in one thread and use it in another thread? Does the CDatabase class need to have the using thread initialized with COM (i.e. CoInitialize et. al.)?

Arjay

sgonepudi
March 1st, 2006, 03:05 PM
Below is the code of DoWork function:

void CPoolItem::DoWork()
{
m_lResult= m_nItem1+m_nItem2;
CDatabase *db=new CDatabase;
try
{
db->OpenEx(_T("DSN=MyDSN;UID=Install;PWD=LiveStrong"),CDatabase::noOdbcDialog);
}
catch(CDBException ex)
{
db->Close();
}
db->Close();
delete db;
*/ SetEvent(hWorkComplete);
}


In the above code i am creating one new CDatabase object for each thread.
there is no relation between 2 threads.

the relation between PoolItem and thread is One-to-One. There is no syncronization problem.

For testing I have created Global database object. and synch objects. But it is giving unpredictable results. some times it is running ok but some times it is giving exceptions. and also these exceptions are coming each time in different places.

Code of DoWork function with Global Database object and Synch object:

void CPoolItem::DoWork()
{
m_lResult= m_nItem1+m_nItem2;

EnterCriticalSection(&theApp.G_CriticalSection);
try
{
theApp.db->OpenEx(_T("DSN=MyDSN;UID=Install;PWD=LiveStrong"),CDatabase::noOdbcDialog);
}
catch(CDBException ex)
{
theApp.db->Close();
}
theApp.db->Close();
LeaveCriticalSection(&theApp.G_CriticalSection);
SetEvent(hWorkComplete);
}

If I remove the database part form the threads it is working fine.

Is there any problem with ODBC driver?

Arjay
March 1st, 2006, 07:55 PM
I'm not surprised that reusing the db object in multiple threads gives you errors - I doubt it's thread safe.

Have you set a break point on the OpenEx function to catch the exception?

I'm running out of ideas, but could you try to initialize COM in your DoWork() function?


void CPoolItem::DoWork()
{
CoInitialize( NULL );

// Original code

CoUninitialize();
}

Arjay