Click to See Complete Forum and Search --> : Thread commuinication


lsvedin
December 16th, 2004, 10:31 AM
I have a application with two threads, the main and a worker. The main has a dialog box that expects data from the worker thread.

If the worker thread simply plunks the data into a member variable of the dialog, everything is hunky-dory, but if the thread attempts to access a function of the dialog, all heck breaks loose and bits start dropping all over the floor.




In the thread code:

CMyApp *m_MainApp = (CMyApp *)AfxGetApp();

m_MainApp->m_MyDlg.m_ThisValue = ThatValue; // Works great

m_MainApp->m_MyDlg.DoThis(USHORT data); //


In the dialog code:

void CMyDlg::DoThis(USHORT data)
{
UpdateData(FALSE); //crashes here
}

Andreas Masur
December 16th, 2004, 10:40 AM
The answer can be found in the following FAQ (http://www.codeguru.com/forum/showthread.php?t=312454)...

lsvedin
December 16th, 2004, 10:45 AM
Andreas, I have already read the FAQ, but my ignorance is getting in my way. What does "A: Since the MFC is not thread-safe at object level (only at class level) one have to be careful when UI elements (like edit controls, listboxes etc.) should be accessed from inside the thread. The safest way is to pass a windows object handle and uses this inside the thread to post messages to the object." mean? I really hate taking up your time for this, but I'd really appreciate any help.

Thanks

Andreas Masur
December 16th, 2004, 10:54 AM
It simply means that you can access different instances of the same class from different threads without any synchronization, however, cannot use a pointer to a 'CWnd' object from a thread other than the one which created the 'CWnd' object...

In your example the problem is the call to 'UpdateData()' which gets called in the context of the thread. This DDX involves 'CWnd' objects which has been created in a different thread, you see what you see...

The soulution is to send a user-defined message such as 'WM_MY_UPDATE' and in reaction to this message in your main thread, call 'UpdateData()' there...

Same applies for any other access to 'CWnd' objects

lsvedin
December 16th, 2004, 11:05 AM
Andreas, the light is beginning to dawn over the hill!!!!!

Thanks!

Andreas Masur
December 16th, 2004, 12:00 PM
You are welcome...

lsvedin
December 16th, 2004, 12:30 PM
Andreas, some more questions about threads.

1. Why was the thread able to set a variable in the dialog object without any problem? The dialog as able to process the data correctly within the main thread process?

2. I have a non-window class and have a timer kicked off by this class using a CALLBACK. This seems to be having trouble accessing a function of the non-window class, is this a pointer problem also?

MrViggy
December 16th, 2004, 04:06 PM
Andreas, some more questions about threads.

1. Why was the thread able to set a variable in the dialog object without any problem? The dialog as able to process the data correctly within the main thread process?
Because that was not a Windows object. In the MFC framework, various thread local maps are setup to help out with window management. When you call a function on a Windows object from another thread, those maps are not accessable, and you wind up asserting, or crashing.

Other data objects (i.e. not Windows objects requiring handles, etc) do not rely on these thread-local data objects. So, you can safely access them.

From the MSDN:
Windows Handle Maps
As a general rule, a thread can access only MFC objects that it created. This is because temporary and permanent Windows handle maps are kept in thread local storage to ensure protection from simultaneous access from multiple threads. For example, a worker thread cannot perform a calculation and then call a document's UpdateAllViews member function to have the windows that contain views on the new data modified. This will have no effect at all, because the map from CWnd objects to HWNDs is local to the primary thread. This means that one thread may have a mapping from a Windows handle to a C++ object, but another thread may map that same handle to a different C++ object. Changes made in one thread would not be reflected in the other.
Viggy

Andreas Masur
December 17th, 2004, 04:01 AM
1. Why was the thread able to set a variable in the dialog object without any problem? The dialog as able to process the data correctly within the main thread process?

As being said....because they are not window classes... :cool:

2. I have a non-window class and have a timer kicked off by this class using a CALLBACK. This seems to be having trouble accessing a function of the non-window class, is this a pointer problem also?
Well...what are you trying to access? In either case (callback function is a static class member function or a global one), it would need a pointer to the actual instance of the class where the variable is declared. However...since you are talking about timers here, I assume you are reffering to the 'TimerProc'...and this is one example of a callback which does not allow to pass a user-defined pointer...anyway...some more information would be nice...

lsvedin
December 17th, 2004, 09:33 AM
Here's the code:In MyDoSomething.h

class uiwTimeManager
{
...
CMyApp *m_pMyApp;
int m_MyInt;
...
void CheckThisOut();
...
}

In MyDoSomething.cpp

CMyDoSomething::CMyDoSomething()
{
m_MyInt= 45;
m_TimerValue = SetTimer(NULL, NULL, TENTH_SEC_TIMEOUT, MyTimer);

}

void CMyDoSomething::CheckThisOut()
{
if(GetThisData() == m_MyInt)
DoThisStuff();
}

void CALLBACK MyTimer(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
CMyApp *pMyApp = (CMyApp *)AfxGetApp();
pMyApp ->m_MyDoSomething.CheckThisOut()
}


When CheckThisOut is called from within MyDoSomething, m_MyInt is 45, but when it called from the timer, m_MyInt is some weird number.

Andreas Masur
December 17th, 2004, 09:41 AM
Two questions...

What does 'GetThisData()' do?
What is 'm_MyDoSomething'?

lsvedin
December 17th, 2004, 10:23 AM
Andreas, thanks for your patience.

GetThisData() calls a function that retrieves data (four ints) from memory and combines them into a single int (newInt), compares newInt with m_MyInt, and returns the result. m_MyInt does change occasionally.

m_MyDoSomething is a member variable of CMyApp:

class MyApp : public CWinApp
{
...
CMyDoSomething m_MyDoSomething ;
...
}

lsvedin
December 17th, 2004, 02:32 PM
Okay, I have found where the problem is, but not what the problem is. It's a memory thing.

My application has several dialogs which are declared in the CMyApp, but are created in CMyView.

I have declared a member variable in my CMyApp:

CDoSomething m_DoSomething;

and in CDoSomething, I declare the member variable

int m_MyInt;

I bring up a dialog which has a pointer to the main app

CMyApp m_pMyApp;

and intializes it

m_pMyApp = (CMyApp *)AfxGetApp();

and then after getting some data from the user, it does

m_pMyApp->m_DoSomething.m_MyInt = nEnteredData;

Everything is running great so far.

Now, I create another dialog and in the OnInitDialog, I read from a file to get some data to put on the dialog and this is where the problem creeps up.

While in debug, I am looking at the memory for m_pMyApp->m_DoSomething.m_MyInt and the data is great, but when I open the file, the memory gets overwritten with garbage.

In the dialog's source file, I have:

#define CAL_FILE "CalibrationData.txt"

BOOL CMyDlg2::OnInitDialog()
{
CDialog::OnInitDialog();

CStdioFile calDataFile;



//Get data for chosen site
if(!calDataFile.Open(CAL_FILE, CFile::modeRead, NULL))
{
<snip>
}
else
{
<snip>
}

//Put string into view
UpdateData(FALSE);
return TRUE;
}
Now, before opening the file, the data is great, but immediately after opening the file, the data has been corrupted. Actually, it's 8 bytes of sequential data gets changed.

Now, if I declare CStdioFile as a member variable and not as a local variable, it works great. or if I just put the test string in the Open function and not CAL_FILE, it works great, but as it is written above, the data gets overwritten.

I know what the symptoms of the problem, but I don't know what is the problem. I know how to fix the problem, but I sure would like to know what the problem could be in order to avoid it in the future.

Andreas Masur
December 18th, 2004, 05:07 AM
I know what the symptoms of the problem, but I don't know what is the problem. I know how to fix the problem, but I sure would like to know what the problem could be in order to avoid it in the future.
Well...this looks like you are writing over the boundaries of an array while reading in the file...and that is what I have assumed earlier....problems like this usually comes down to memory overwrite...

You did not post the code where you actually read from the file...thus, I cannot check anything here...

While it *seems* to work if you move the class to the member area would be explainable as well...this will change the memory location of the class...and writing over the boundaries in that area might not cause immediate problems...

lsvedin
December 20th, 2004, 08:07 PM
After some pretty severe debugging, I finally figured out the problem. There was a memcpy on another thread that was suppose to be copying only 40 bytes of data and it was plopping down 52 bytes of data and it was just wiping me right out. It was a bear to find.

Hey Andreas, thanks for the help. :wave:

Andreas Masur
December 21st, 2004, 04:27 AM
You are welcome....and for your next memory leak...

Detecting and Isolating Memory Leaks (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxcondetectingisolatingmemoryleaks.asp)
Detecting Memory Leaks in MFC (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/_core_detecting_memory_leaks.asp)