Problem: Creation of a modless CDialog in a thread.
Hello,
I’m developing an application composed of three threads:
- The “main” thread, with the user interface and other stuff.
- A “working” thread that handles messages to perform actions.
- A “monitoring” thread that is used to load / unload monitoring panels.
The “main” thread starts both “working” and “monitoring” threads.
The user can ask to open/close monitoring panels, so the “main” thread posts messages to the “monitoring” thread. When a panel is opened or closed, it registers/unregisters itself to the “working” thread for notifications about the performed actions.
When the application is closed, the “main” thread stops the “monitoring” and the “working” threads by posting messages.
In normal processing, every thing works fine.
My problem is the following:
When a message is posted to the “monitoring” thread during a panel creation (panels are modless CDialog), the “monitoring” thread blocks on the Create(IDD); of the CDialog. More precisely in dlgcore.cpp, in method:
BOOL CWnd::CreateDlgIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd, HINSTANCE hInst)
on line:
hWnd = ::CreateDialogIndirect(hInst, lpDialogTemplate, pParentWnd->GetSafeHwnd(), AfxDlgProc);
This problem is very easy to reproduce if I use following code:
::PostThreadMessage ( monitoringThreadId, MSG_OpenPanel, 0, 0 );
::PostThreadMessage ( monitoringThreadId, MSG_ClosePanel, 0, 0 );
In my log I can see the both post message. Only the first one handled and its processing stay blocked on the CDialog Create(IDD) method, as explained above.
Thanks for your help.
Re: Problem: Creation of a modless CDialog in a thread.
Why do you have a "monitoring" thread at all? Do all your user interface processing in the main thread. The only thing a worker thread should do with user interfaces is post messages to windows.
Re: Problem: Creation of a modless CDialog in a thread.
As D_Drmmr already pointed out you should move all the UI attributes into the main GUI thread.
Besides:
Quote:
Originally Posted by
Cariboo42
... I use following code:
Code:
::PostThreadMessage ( monitoringThreadId, MSG_OpenPanel, 0, 0 );
::PostThreadMessage ( monitoringThreadId, MSG_ClosePanel, 0, 0 );
In my log I can see the both post message. Only the first one handled and its processing stay blocked ...
You could only PostThreadMessage is the thread you were posting to had the message pump. "Worker" thread has no message pump. Only MFC so called "UI" thread has.
You may want to look at the MDSN articles:
PostThreadMessage Function (note its Remark section)
AfxBeginThread
as well as Using Worker Threads essay
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
D_Drmmr
Why do you have a "monitoring" thread at all? Do all your user interface processing in the main thread. The only thing a worker thread should do with user interfaces is post messages to windows.
In fact, I develop an application with plug-ins and I decided to use Threads (and PostThreadMessages) for communication with my plug-ins. It also prevents the “main” thread to freeze due to time-consumer plug-ins.
What I really want to understand is why a thread is blocking when it receives a message while it is creating a CDialog.
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
Cariboo42
...
What I really want to understand is why a thread is blocking when it receives a message while it is creating a CDialog.
Again:
because a worker thread was not designed to handle thread messages. Just because it does not have message pump.
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
VictorN
You could only PostThreadMessage is the thread you were posting to had the message pump. "Worker" thread has no message pump. Only MFC so called "UI" thread has.
I use CWinThread and everything works fine. I develop and use my application for about one month. I discovered this bug two days ago, when exiting the application, just after a panel creation.
Then, after log analysis and two days of work, I found out that this bug occurs only when a message is posted the same time the thread was creating a CDialog.
I think this can be due to a conflict when the message is added to the thread message pump and, at the same time, the CWinThread add the CDialog management to its message pump.
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
VictorN
Again:
because a worker thread was not designed to handle thread messages. Just because it does not have message pump.
Ok, we misunderstood. I use only CWinThread but I named this thread "working" thread because it has associated windows.
Also the "working" thread works fine. The problem lies in the "monitoring" thread.
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
Cariboo42
Ok, we misunderstood. I use only CWinThread but I named this thread "working" thread because it has associated windows.
Also the "working" thread works fine. The problem lies in the "monitoring" thread.
Did you read my post#3? Did you read the MSDN article AfxBeginThread?
How did you create your "monitoring" thread?
And again: why create a dialog in the secondary thread? :confused: :confused: :confused:
Re: Problem: Creation of a modless CDialog in a thread.
Sorry: ... it has NO associated windows.
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
VictorN
Did you read my post#3? Did you read the MSDN article AfxBeginThread?
How did you create your "monitoring" thread?
And again: why create a dialog in the secondary thread? :confused: :confused: :confused:
I created the thread using the CreateThread method, to have a message pump and then to be able to use PostThreadMessage.
My application mount/unmount modules and a need to create a thread for each module I mount. A module must be able to create dialog boxes.
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
Cariboo42
I created the thread using the CreateThread method, to have a message pump ...
Do you mean CWinThread::CreateThread or ::CreateThread API?
How exactly? Could you show your code?
Re: Problem: Creation of a modless CDialog in a thread.
Code:
// Main thread: Creation of MonitorThread.
CMonitorThread* pMonitor = new CMonitorThread (); // Derived from CWinThread.
if ( pMonitor == NULL )
{
// Error management.
}
if ( pMonitor->CreateThread () == FALSE )
{
// Error management.
}
pMonitor->PostThreadMessage ( MSG_CreatePanel, 0, 0 );
#ifdef DO_BUG
Sleep ( 0 );
pMonitor->PostThreadMessage ( MSG_DestroyPanel, 0, 0 );
#endif
// Monitor thread: Hanlde the MSG_CreatePanel.
void CMonitorThread::OnCreatePanel ( WPARAM wParam, LPARAM lParam )
{
m_pPanel = new CMonotoringPanel ();
if ( m_pPanel == NULL )
{
// Error management.
}
if ( m_pPanel->Create ( IDD ) == FALSE )
{
// Error management.
}
m_pPanel->ShowWindow ( SW_SHOW );
}
DO_BUG not defined:
Works fine: The thread is created, the dialog box too.
DO_BUG defined:
Thread is created and both PostThreadMessage executed successfully (I checked the return value).
First message (MSG_CreatePanel) has been received, but m_pPanel->Create(IDD) blocks, as explaind in my original post.
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
Cariboo42
In fact, I develop an application with plug-ins and I decided to use Threads (and PostThreadMessages) for communication with my plug-ins. It also prevents the “main” thread to freeze due to time-consumer plug-ins.
IMO this is a bad design. Multi-threaded code is hard to debug and it's easy to introduce errors if a programmer doesn't know how to do MT programming correctly. When you have little or no control over some part of the code that will be executed by your program, you should try to prevent these problems. Thus, if the plug-ins need to create windows, let them do it in the main thread. If a plug-in has time-consuming operations, it should create its own worker thread to prevent the application from becoming non-responsive.
Quote:
Originally Posted by
Cariboo42
What I really want to understand is why a thread is blocking when it receives a message while it is creating a CDialog.
Maybe this will help: http://support.microsoft.com/kb/183116
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
D_Drmmr
Thanks. It helps to understand how dialog message pump works and the problems inherent to such design when using in threads. But this concerns essentially modal dialogs. IMO, it should work with modless dialogs, but what I also understand is that modless CDialog creation is not multi-thread safe regarding to PostThreadMessage.
Quote:
Originally Posted by
D_Drmmr
IMO this is a bad design. Multi-threaded code is hard to debug and it's easy to introduce errors
Indeed, it seems to be a bad design to create windows in separate threads. So I’ll redesign my application that way.
But I do not agree with the fact that MT programming is hard to debug. It’s like everything else: It requires being rigorous. :)
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
Cariboo42
Thanks. It helps to understand how dialog message pump works and the problems inherent to such design when using in threads. But this concerns essentially modal dialogs. IMO, it should work with modless dialogs, but what I also understand is that modless CDialog creation is not multi-thread safe regarding to PostThreadMessage.
No, that page is not limited to modal dialogs. It talks about modal behavior and explicitly mentions moving or resizing the window as examples. I'm not sure, but it could be that dialog creation is modal behavior and therefore, the posted thread message is lost.
Quote:
Originally Posted by
Cariboo42
But I do not agree with the fact that MT programming is hard to debug. It’s like everything else: It requires being rigorous. :)
That's the first time I've heard anyone say debugging MT code is not hard. By its very nature MT-related bugs are not strictly reproducible, which by itself makes them hard to debug in my book. Also AFAIK, VS does not offer much support for debugging MT-related issues, unlike the tools offered to track memory leaks, iterator/bounds validation, memory values set by debug heap allocator, etc.
So, I'm curious what makes you say that MT programming is not hard to debug? Which tools/methods do you use?
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
Cariboo42
but I do not agree with the fact that MT programming is hard to debug. It’s like everything else: It requires being rigorous. :)
Tell that to the many companies that will refuse to hire any programmer that doesn't know how to develop/debug MT apps, regardless of how much those programmers may be wizards at developing/debugging single-threaded apps.
Regards,
Paul McKenzie
Re: Problem: Creation of a modless CDialog in a thread.
In MFC, creation of a dialog (indeed, the creation of ANY window) involves the temporary installation of a hook (a CBT hook, to be precise), in order to hook into the window creation process and to link the CWnd object to the HWND window. This allows MFC to handle some of the very earliest messages posted to the window, such as WM_NCCREATE, since otherwise the messages would not yet be linked into the MFC message map system via the CWnd object.
It comes as no surprise that back-to-back posting of PostThreadMessage might interfere with this process, although I cannot now think of a reason that the call to CWnd::Create might block.
Anyway, as explained in the link given by D_Drmmr, and as also explained in the docs for PostThreadMessage, it's almost always a mistake to call PostThredMessage to a thread that displays any visible UI. The reason involves loss of such messages at any time when the UI is in a modal message loop, such as during re-sizing or dragging operations. So it's probably best to go with your re-design, where all UI is handled by a single thread.
Mike
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
D_Drmmr
So, I'm curious what makes you say that MT programming is not hard to debug? Which tools/methods do you use?
First of all, I think a good idea is to use a method to design applications.
Personally I use UML with a lot of sequence diagrams for multi-threaded project, with every possible case. This prevents from architecture problems and allows to clearly identify all cases (normal processing, error processing...).
Then, simply code what the diagrams shows with rigour. Check all return values, correctly manage all error cases... Just like for memory management: You check allocations, buffer sizes, free all allocated memory... MT coding is just the same.
Working this way, MT bugs are very rare and easy to found: Just check your design to find out what happen or if a case is not missing... Check that the code corresponds to the design.
Another tool I use is a log-file object that also log timestamp and thread id. With the possibility to disable parts of the logs that are not significant and can alter the MT behaviour. (My logs are organized in log domains. Generally one log domain for one class.)
Personally, I rarely use log-file for debugging. It is more a way for me to control the application behaviour during tests.
At last, if a problem is not strictly reproducible, it is always possible to automate application then analyze logs after lunch or in the morning.
I encountered one random bug once in the past, I work in a company that develop a client-server survey software with a mass multi-threaded server (threads for address research and automatic phone calls, for clients requests management, for answers analysis and recording, for live database analysis, and more). The problem happened once a week in normal process and every night with our automated process. After two weeks of investigation, the bug was not due to MT but to a memory allocation problem: a bug in a buffer size computation lead to a very rare buffer overflow and other data to be corrupted.
It is the reason why I do not think MT development is not harder to code and develop than every thing else. We just need to be rigorous in our work. :)
It’s just my feeling about it and your experience can be different.
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
MikeAThon
Anyway, as explained in the link given by D_Drmmr, and as also explained in the docs for PostThreadMessage, it's almost always a mistake to call PostThredMessage to a thread that displays any visible UI. The reason involves loss of such messages at any time when the UI is in a modal message loop, such as during re-sizing or dragging operations. So it's probably best to go with your re-design, where all UI is handled by a single thread.
Ok. Now, I have a better understanding of how Windows manages messages.
I’ll redesign my application that way this week-end. Fortunately not a big redesign. :)
Thanks to all.
(and sorry for my bad English. Hope it didn’t hurt your eyes too hard :) )
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
Cariboo42
Personally I use UML with a lot of sequence diagrams [emphasis added by Mike] for multi-threaded project, with every possible case. This prevents from architecture problems and allows to clearly identify all cases (normal processing, error processing...).
...It is the reason why I do not think MT development is not harder to code and develop than every thing else. We just need to be rigorous in our work. :)
One possible problem with this approach, and one reason why multi-threaded coding/debugging is much more difficult than its singly-threaded counterpart, is that execution of multi-threaded code, by multiple processors, is non-deterministic and most definitely NOT sequential. Sequence diagrams, which emphasize sequential processing, are in my mind of little help when it comes to multi-threaded development.
One man's view, anyway.
Mike
Re: Problem: Creation of a modless CDialog in a thread.
Quote:
Originally Posted by
Cariboo42
It is the reason why I do not think MT development is not harder to code and develop than every thing else.
Then again, why do most in the industry will not hire programmers who has no MT experience when MT experience is required, regardless of the other programming experience the programmer may have?
To these companies, it is imperative that the person knows, not in passing, but have a wealth of knowledge of MT design, debugging, and maintenance. And again, you can have decades of "single-threaded" experience, you just will not make it in the real MT world on single-thread experience and a week or two of reading a book.
Usually, you gain MT experience if you're in a company that currently have ST apps that are being converted to MT due to various circumstances. Otherwise, and this is in the areas of the United Statesd I have been to, it is very difficult to find anyone willing to hire ST programmers to step into a MT job just like that.
The obvious reason for this is that MT programming is, to put it simply, not easy or even easily grasped by many experienced ST coders. It does require experience, knowing which synchronization objects to use and when, diagnosing errors such as race conditions, what are MT safe constructs and which are not, etc. Not small topics at all...
Regards,
Paul McKenzie