Thread: EnterCriticalSection and MessageBox

  1. #1
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868

    EnterCriticalSection and MessageBox

    I have an app that downloads data of very different sizes from a server.
    I use a single thread to process the downloads. One type of data is very large and has very important header info in the first few bytes.

    I create a EnterCriticalSection() as the socket buffer is translated and call LeaveCriticalSection() when all the current data has been dealt with.

    (the CRITICAL_SECTION variable has been init only once as the app starts)

    This works perfectly until I add the MessageBox().

    In debugging I added a MessageBox to inform me the first part of the data had been successfully translated (and uncompressed). Some of the data is still to be translated, it is only the first part of the data that has been delt with (I get a partial read as it is from multiple writes to the socket and the data size is large).

    The function should return to the data translation function and use the rest of the buffer to fill in the partial data before again trying to read more data from the socket.

    At this point the app seems to create another thread for the MessageBox allowing the rest of the app to continue, without using the partial data in the translate function. (that is the thread that calls MessageBox() does not continue but does not stop ). LeaveCriticalSection() does not get called this time.

    The rest of the data is read from the socket (by the original thread) and sent to the function to translate it. Unfortunately some data is still in the function waiting for the user to press the OK button on the MessageBox.

    The function should not be able to enter the critical section but the thread that calls is the same as the one ALREADY in the function! That is the original thread (that 'Entered' the critical section) has not exited but is again at the start of the function asking to enter the critical section again.
    EnterCriticalSection() thinks that it is a recursive function and allows the thread to continue, causing a 'very BAD THING' to happen.

    As I said if the MessageBox is not there the function works as expected / desired (as it does with my own version of a messagebox ie a dialog with a static text displayed on it).

    Is there something I am doing wrong?

    Any explanations / ideas why the MessageBox can do this?

    Code:
    //data read from socket
    EnterCriticalSection(&CS);
    while (iUsed<iRead)
    {
             //read header, alloc memory and process
             //read data and fill memory
             if(iNeeded>=iRead-iUsed) //not enough data in current buffer
             {
                      //save memory offset 
                      //partiallly fill buffer
             }
             else//have all the data
             {
                      //fill buffer
                      //process data
                      MessageBox(hWnd, "Data processed.", szApp, MB_OK);
             }
             iUsed+=Data_Copied;
    }
    LeaveCriticalSection(&CS);
    return;
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  2. #2
    fou
    Guest
    The problem may be caused by the fact that the MessageBox has its own message loop. This allows the windows in your app to be painted, closed, etc while the message box is displaying.

    It also means that if your function is triggered by a message and for some reason the message is triggered while a message box is displayed that your function will be called again.
    This all happens in the original thread and processing continues at the original point once the Ok button has been pressed. For this reason it is also always important to check that any window that you rely on still exists after a call to MessageBox.

    For an example you can think of the messagebox function looking something like this:
    Code:
    MessageBox() {
    
    	MSG Msg; 
    
    	//display dialog.
    	while ( PeekMessage(&Msg,NULL,0,0,PM_REMOVE)
    		&& !okButtonPressed ) {
    		TranslateMessage(&Msg); 
    		DispatchMessage(&Msg);
    	}
    }
    Have Fun!

  3. #3
    It's full of stars adrianxw's Avatar
    Join Date
    Aug 2001
    Posts
    4,829
    If you are using an NT cored system, you can Ctrl-Alt-Del and on the processes page, use View->Select Columns to include the thread count of your process. You will then at least know if the thread count is increasing when the MessageBox is active.

    It is difficult to investigate this without a working, (or, non-working!), example.

    I have to say, I routinely multithread my programs, sometimes hugely so, and have never had a problem with a CS. Of course, that may simply be because our applications are different.
    Wave upon wave of demented avengers march cheerfully out of obscurity unto the dream.

  4. #4
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Thanks. I realy just wanted to know why (or if it was me, seems it is me).

    Seems the messagebox creates a second thread to execute in, waiting for the user to respond. It kills the original thread or allows it to return to the main message loop to get a new message. If it was not the original thread I would not have a problem.

    When the user responds to the messagebox a third thread is created to continue the original threads work.

    My problem is that the original thread is allowed to restart. This means (as it is the original thread) that it can enter the critical section again.

    If I make the app wait for the user by holding the original thread at the critical section (as the call to release the critical section has not yet been executed) the app stops responding and the user can't click the messagebox.

    Simple solution -> don't use messagebox

    >>It is difficult to investigate this without a working, (or, non-working!), example.

    Yes. Too much involved to cut this section out and still reproduce the error. I rewrote the section three times trying to find the error.

    >>This allows the windows in your app to be painted, closed, etc while the message box is displaying.

    Even if I set it system modal (or app modal) it allows the app to read from the network and process the data. Why dag-nab-it?
    How does it release the original thread in a critical section without it leaving the critical section? ie the same thread enters the same critical section a second time before it exits.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  5. #5
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    I've had similar problems with MessageBox's, so much that I had to code a replacement for it, which worked, though I opted to go with a "blocking" version (since this is usually what I want the message box for: to block execution till it is processed), though I suspect that even a multithreaded one would work too (just a hunch though).
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  6. #6
    It's full of stars adrianxw's Avatar
    Join Date
    Aug 2001
    Posts
    4,829
    I am intruiged by this. I have not had a problem, BUT, I don't think I would ever have used a MessageBox in a CS. Normally, I want as little as possible inside the CS construction as, being in it, I am blocking other threads that want the same CS. If I want to know something that is happening inside the construction, I use PostMessage() to my main loop and have a handler there to display/do something.

    Presumably, the other threads waiting on the CS are still blocked when this happens?

    (I'm trying to fiddle one of my apps so I can see this problem).
    Wave upon wave of demented avengers march cheerfully out of obscurity unto the dream.

  7. #7
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Hmm...it seems to work ok for me......

    Code:
    #include <windows.h>
    #include "resource.h"
    
    
    class CriticalSection{
    	CRITICAL_SECTION m_cs;
    public:
    	CriticalSection(){InitializeCriticalSection(&m_cs);}
    	~CriticalSection(){DeleteCriticalSection(&m_cs);}
    	void Enter(){EnterCriticalSection (&m_cs);}
    	void Leave(){LeaveCriticalSection (&m_cs);}
    }cs;
    
    DWORD WINAPI ThreadProc1(LPVOID lpv)
    {
    	cs.Enter();
    	HWND hWnd = reinterpret_cast<HWND>(lpv);
    	MessageBox(hWnd,"Hello from Thread1","",MB_OK);
    	cs.Leave();
    	return 0;
    }
    
    int CALLBACK DialogProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
    {
    	DWORD dwDummy;
    	static HANDLE hThreads[3];
    
    	switch(uMsg){
    	case WM_INITDIALOG:
    		hThreads[0] = CreateThread(0,0,ThreadProc1,reinterpret_cast<LPVOID>(hWnd),0,&dwDummy);
    		hThreads[1] = CreateThread(0,0,ThreadProc1,reinterpret_cast<LPVOID>(hWnd),0,&dwDummy);
    		hThreads[2] = CreateThread(0,0,ThreadProc1,reinterpret_cast<LPVOID>(hWnd),0,&dwDummy);
    		break;
    	case WM_CLOSE:
    		EndDialog(hWnd,0);
    		break;
    	}
    
    	return 0;
    }
    
    int WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR,int)
    {
    	return DialogBox(hInst,MAKEINTRESOURCE(IDD_DIALOG1),0,DialogProc);
    }
    If I comment out the Leave/Enter, then all MessageBox's are created at the same time.....otherwise they are created in succession.....as expected....sooooooo??

    Pertty weird what's going on........

  8. #8
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    This is very strange....I followed the thread through the MessageBox func (just walking through the assembler) and it didnt create any new threads before during and after execution....dont know what I'm doing different?

  9. #9
    It's full of stars adrianxw's Avatar
    Join Date
    Aug 2001
    Posts
    4,829
    I, also, have not been able to show this effect. I have bodged up an old massively paralleled file processing system to throw up MessageBoxes, apart from being a damn pain to drive, it still seems to work.

    Novacain:

    Are you using asynchronous I/O routines in your reads?
    Wave upon wave of demented avengers march cheerfully out of obscurity unto the dream.

  10. #10
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Thanks for the help!

    As a patch I ported the messages to a status bar rather than the messagebox and there is no problem.

    >>Are you using asynchronous I/O routines in your reads?

    Yes.

    The problem does not occur if the data 'chunk' is smaller than the socket buffer. I am using 32K buffers and getting 120K-140K of compressed data (wav files) written in 4 writes (header, data, ....) There is no problem for multiple smaller requests (images and data up to the buffer size).


    At the moment there is a problem in the remote site and I can't get to the data to investigate more. (So many bushfires in Australia and we have been rained out!)



    >>I followed the thread through the MessageBox func (just walking through the assembler) and it didnt create any new threads before during and after execution

    On Win2000 my task manager tells me that there is an increase in the threads as soon as the messagebox appears. Then it quickly returns to 'normal'.

    So what is creating the thread? (as this is where my problem lies)
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  11. #11
    fou
    Guest
    The problem does not occur if the data 'chunk' is smaller than the socket buffer. I am using 32K buffers and getting 120K-140K of compressed data (wav files) written in 4 writes (header, data, ....) There is no problem for multiple smaller requests(images and data up to the buffer size).
    What async method are you using?

    If you are using the message based async IO then what is probably happening is the message for the next read is being processed when the message box is displayed.
    ie.
    1. You read some data.
    2. Windows sees there is still data available and posts the data available message (FD READ)
    3. The message loop in the messagebox function dispatches this message.
    4. Your code starts again.

    If you are not using the message based async IO could you indicate which method you are using?

  12. #12
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Originally posted by novacain
    >>I followed the thread through the MessageBox func (just walking through the assembler) and it didnt create any new threads before during and after execution

    On Win2000 my task manager tells me that there is an increase in the threads as soon as the messagebox appears. Then it quickly returns to 'normal'.

    So what is creating the thread? (as this is where my problem lies)
    Hmm....ok...try compile the following code....its a cheap little debugger I made to track the creation and destruction of threads in a process.......it ignores the primary thread....but will show all threads created (with PID and the point where the thread was created) and when they die (along with exit code).....

    Now run it on the command line with the path to your exe file.....whenever a thread is created you get a notification.....
    Its not perfect...but it might help

    Code:
    #include <windows.h>
    #include <iostream>
    #include <iomanip>
    
    
    int main(int nArg,char** szArgz)
    {
    	if(nArg != 2)
    	{
    		std::cout << "1 argument only" << std::endl;
    		std::cout << "Specify name of program to track" << std::endl;
    		return 1;
    	}
    	STARTUPINFO si = {0};
    	si.cb = sizeof(si);
    	si.wShowWindow = SW_SHOWDEFAULT;
    	PROCESS_INFORMATION pi = {0};
    
    	if(!CreateProcess(szArgz[1],0,0,0,0,DEBUG_PROCESS | DETACHED_PROCESS,
    		0,0,&si,& pi))
    	{
    		std::cout << "Unable to open " <<  szArgz[1] << std::endl;
    		return 1;
    	}
    	std::cout << "Opened " << szArgz[1] << std::endl;
    
    	DEBUG_EVENT de = {0};
    	bool bRun = true;
    
    	while(bRun && WaitForDebugEvent(&de,INFINITE) )
    	{
    		switch(de.dwDebugEventCode)
    		{
    		case CREATE_THREAD_DEBUG_EVENT:
    			std::cout << std::hex << "New Thread - " << de.dwThreadId;
    			std::cout << " Started at address - ";
    			std::cout << reinterpret_cast<DWORD>(de.u.CreateThread.lpThreadLocalBase);
    			std::cout << std::endl;
    			break;
    		case EXIT_THREAD_DEBUG_EVENT:
    			std::cout << std::hex << "Thread Exited - " << de.dwThreadId;
    			std::cout << " Exited with value of - ";
    			std::cout << de.u.ExitThread.dwExitCode ;
    			std::cout << std::endl;
    			break;
    		case RIP_EVENT:
    			std::cout << "Weird debug error - bailing out!" << std::endl;
    			bRun = false;
    			break;
    		case EXIT_PROCESS_DEBUG_EVENT:
    			std::cout << "The debugged process ended" << std::endl;
    			bRun = false;
    			break;
    		}
    		ContinueDebugEvent(de.dwProcessId,de.dwThreadId,DBG_CONTINUE); 
    	}
    
    	std::cout << "Debugging ends";
    
    
    
    
    	return 0;
    }
    As an asside...try run it for Internet explorer and see how many threads that beast needs to run!!

  13. #13
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Hmm...funny....the last param of my CreateProcess call is the address of my PROCESS_INFORMATION struct

    But if you use the addressof operator an pi...this board changes iot to the mathamatical pi sign

    Didnt know that

    &pi != & pi

  14. #14
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Whoops...error in what I expected from the code...the address specified isnt what I expected....but it will show you the created threads and at what point they are created...and at what point they die....

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Delete files that are used
    By Yuri in forum C++ Programming
    Replies: 8
    Last Post: 10-18-2005, 01:48 PM
  2. problem with A simple modeless messagebox
    By hanhao in forum C++ Programming
    Replies: 8
    Last Post: 07-05-2005, 11:18 PM
  3. quick messagebox question
    By Benzakhar in forum Windows Programming
    Replies: 2
    Last Post: 03-09-2004, 10:20 PM
  4. Forcing MessageBox to be non-concurrent?
    By JasonD in forum Windows Programming
    Replies: 17
    Last Post: 12-10-2003, 03:16 PM
  5. double in a MessageBox
    By Robert602 in forum Windows Programming
    Replies: 3
    Last Post: 12-17-2001, 03:10 PM