views:

321

answers:

8

I have written a multithreaded program which does some thinking and prints out some diagnostics along the way. I have noticed that if I jiggle the mouse while the program is running then the program runs quicker. Now I could go in to detail here about how exactly I'm printing... but I will hold off just for now because I've noticed that in many other programs, things happen faster if the mouse is jiggled, I wonder if there is some classic error that many people have made in which the message loop is somehow slowed down by a non-moving mouse.

EDIT: My method of "printing" is as follows... I have a rich edit control window to display text. When I want to print something, I append the new text on to the existing text within the window and then redraw the window with SendMessage(,WM_PAINT,0,0).

Actually its a bit more complicated, I have multiple rich edit control windows, one for each thread (4 threads on my 4-core PC). A rough outline of my "my_printf()" is as follows:

void _cdecl my_printf(char *the_text_to_add)
{
    EnterCriticalSection(&my_printf_critsec);
    GetWindowText(...); // get the existing text
    SetWindowText(...); // append the_text_to_add
    SendMessage(...WM_PAINT...);
    LeaveCriticalSection(&my_printf_critsec);
}

I should point out that I have been using this method of printing for years in a non-multithreaded program without even noticing any interaction with mouse-jiggling.

EDIT: Ok, here's my entire messageloop that runs on the root thread while the child threads do their work. The child threads call my_printf() to report on their progress.

for(;;)
{
    DWORD   dwWake;
    MSG msg;

    dwWake = MsgWaitForMultipleObjects(
                            current_size_of_handle_list,
                            hThrd,
                            FALSE,
                            INFINITE,
                            QS_ALLEVENTS);

    if (dwWake >= WAIT_OBJECT_0 && dwWake < (WAIT_OBJECT_0 + current_size_of_handle_list))
    {
        int index;
        index = dwWake - WAIT_OBJECT_0;
        int j;
        for (j = index+1;j < current_size_of_handle_list;j++)
        {
            hThrd[j-1] = hThrd[j];
        }
        current_size_of_handle_list--;
        if (current_size_of_handle_list == 0)
        {
            break;
        }

    }
    else if (dwWake == (WAIT_OBJECT_0 + current_size_of_handle_list))
    {
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    else if (dwWake == WAIT_TIMEOUT)
    {
        printmessage("TIMEOUT!");
    }
    else
    {
        printmessage("Goof!");
    }
}

EDIT: Solved! This may be an ugly solution - but I just changed the timeout from infinite to 20ms, then in the if (dwWake == WAIT_TIMEOUT) section I swapped printmessage("TIMEOUT!"); for:

while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

I'm not closing this question yet because I'd still like to know why the original code did not work all by itself.

A: 

Are you completely sure that the program really runs faster? Or is it output that is refreshed more frequently?

janislaw
I'm sure that the non-GUI aspects of the program are running at full speed - but it appears that the process of printing is somehow held-up by the mouse not moving. I am going to edit some more detail in to my original question... it will take a minute or two.
Mick
+1  A: 

Well I can't totally help you because we don't have enough info but I had a similar problem where my application would not refresh unless I moved the mouse or after some (not unsignificant) delay.

When investigating the problem I found that basically, the GUI thread will sleep if there is no more messages to process. Jiggling the mouse will create new windows messages to be sent to the windows, waking the thread from sleep.

My problem was that I was doing my processing in the OnIdle (MFC, not sure about you) function, and that, after doing the processing one time, the thread would go to sleep.

I don't think that is your problem since you seems to post a windows message (WM_PAINT) from your thread, what I wasn't doing in my case (which should wake up the gui thread) but maybe this can help you get in the right direction to solve your problem?

Edit: I though about it a little, maybe there is a special case for WM_PAINT (like you forget to call Invalidate or something, I'm not an expert in windows programming) so maybe try to post another message like WM_USER to your application and see if it fix your problem (this should be sure to wake up the gui thread I think). Also posting the full call to the SendMessage function could help.

Edit2: Well, after seeing your comment to Kelly French above you seems to have exactly the same symptoms I had so I would guess that, for whatever reason, your call to PostMessage do not seems to wake up the gui thread or something similar. What are you passing for first argument to PostMessage? What I did in my case was to call PostMessage with argument WM_USER, 0, 0 to my app. You can also try the PostThreadMessage variant while keeping the current threadID of the main thread in a variable (see GetCurrentThreadId).

Also you could try to call Invalidate on your object. Windows keeps memory of if an object need to be repainted and will not do it if it is not needed. I do not know if a direct call to WM_PAINT override this or not.

Well that's all I can think of. At least you found a fix even if it is not the most elegant.

n1ck
See more detail added to my original post.
Mick
Well the function MsgWaitForMultipleObjects seems to be waken up when it receive an input (QS_ALLEVENTS), so It make me think that your WM_PAINT message is not received (for whatever reason) or do not cause the thread to wake up. I would check the SendMessage function. It seems to contains a lot of particularity. Maybe try a variant like PostMessage or something? I'm not sure but it looks like we're close to the problem.
n1ck
Just this second tried PostMessage() - but left with exactly the same symptoms.
Mick
What about trying to post a different message? Did you try with WM_USER like I told in my edit? See if it helps.
n1ck
(Send both messages)
n1ck
Just tried WM_USER - that didn't do the trick.
Mick
That last message should have been, "just tried sending both"
Mick
+1  A: 

If I remember correctly, WM_PAINT is a very low priority message, and will only get relayed when the message queue is otherwise empty. Also, Windows will coalesce multiple WM_PAINT messages into one. I could see the mouse movement resulting in fewer redraw events, each handling a larger update, thus improving performance.

LnxPrgr3
A: 

Are you using SendMessage or PostMessage? I'm curious if perhaps switching to the other will make things work "better" in this particular environment.

Taken from developerfusion:

There’s another similar API which works exactly like SendMessage and that is PostMessage API. Both require same parameters but there’s a slight difference. When a message is sent to a window with SendMessage, the window procedure is called and the calling program (or thread) waits for the message to be processed and replied back, and until then the calling program does not resume its processing. One thing is wrong with this approach however, that is if the program that is busy carrying out long instructions or a program that has been hung and hence no time to respond to the message will in turn hang your program too because your program will be waiting for a reply that may never arrive. The solution to this is to use PostMessage instead of SendMessage. PostMessage on the otherhand returns to the calling program immediately without waiting for the thread to process the message, hence saving your program from hanging. Which of them you have to use depends on your requirement.

jvenema
I'm using SendMessage - I did try PostMessage but it made no difference to the problem.
Mick
A: 

Is your GUI window maximized? Does it happen whether the mouse movement happens over your app window or over some other window like another app or the desktop? When the mouse moves over your app, the mouse_move messages get sent to your message queue. This may wake up the thread or be forcing a WM_PAINT message.

I doubt that the printing is actually going faster. I suspect that the increased number of messages caused by the mouse movement is forcing more window invalidation events so the text updates are happening on a more granular basis. When the mouse isn't being moved does the printing happen in larger blocks, say blocks of 20 characters vs 5 characters at a time?

Could you clarify what you mean by faster printing? Is it absolute, like 100 characters per minute vs 20 characters per minute? Or is it more like 100 characters per minute either way but they show up in blocks when the mouse is still?

Kelly French
If the mouse is stationary then printing barely occurs at all. If I wait a long time then just touch the mouse ever so slightly, then all the printing suddenly appears. It doesn't appear to matter where the mouse is located. None of the windows are maximized.
Mick
+1  A: 

I think it has to do with processing in foreground and background. If the operating system thinks your window is not top priority it shifts your job to background. If you are forcing the window to be on top, it will put all its resources to work on your window and drops the other items it is working on. Its real and its been here since DOS. http://en.wikipedia.org/wiki/Foreground-background You might try calling the following at critical times in your code.

Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
gerard
A: 

One possibility is that you are seeing the effect of the OS doing thread priority boosting / retarding for certain GUI messages.

I am assuming you have one ”GUI & Other Stuff” thread, and multiple worker threads. When there is no GUI activity, the “Other Stuff” thread goes to a lower priority. When you wiggle the mouse or timeout, the “Other Stuff” thread goes to a higher priority.

Changing the worker threads to a lower priority and then wiggling the mouse, would confirm or refute this.

jyoung
+3  A: 

i can see 3 problems here:

  1. the documentation for WM_PAINT says: The WM_PAINT message is generated by the system and should not be sent by an application. unfortunately i don't know any workaround, but i think SetWindowText() will take care of repainting the window, so this call may be useless.

  2. SendMessage() is a blocking call and does not return until the message has been processed by the application. since painting may take a while to be processed, your program is likely to get hanged in your critical section, especially when considering my 3rd point. PostMessage() would be much better here, since you have no reason to need your window to be repainted "right now".

  3. you are using QS_ALLEVENTS in MsgWaitForMultipleObjects(), but this mask DOES NOT include the QS_SENDMESSAGE flag. thus your SendMessage() call is likely ignored and does not wake your thread. you should be using QS_ALLINPUT.

can you check the behavior of your application with an INFINITE timeout and the above 3 modifications included ?

Adrien Plisson
The important thing is that the SendMessage will wait until message processing function (PeekMessage in this case) is called on the thread owning the target window. Because the MsgWait* is not woken by the SendMessage alone based on those flags, it will wait until some non-sent message is generated. The second important thing is that the GetWindowText and SetWindowText internally do SendMessage.
Komat
Well done! The answer was your number 3.
Mick