If a Menu is accessed and the application used the function SetTimer, then the application and the wineserver use 100% of the CPU (loop of sending and receiving WM_TIMER).
function used: SetTimer(NULL, 0, 100, TimerProc)
Description of the loop: dlls/user/menu.c MENU_TrackMenu
while (!fEndMenu) { ... for (;;) { (1) if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE )) ... { (2) DispatchMessageW( &msg ); } if (!fEndMenu) fRemove = TRUE;
/* finally remove message from the queue */
if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) ) (3) PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); else mt.trackFlags &= ~TF_SKIPREMOVE; }
(1) The WM_TIMER is peeked and the timer is not restarted (PM_NOREMOVE) (2) The WM_TIMER is dispatched here (3) The WM_TIMER is only removed from the queue. The next PeekMessageW (1) return immediately with WM_TIMER since the timer was not restarted.
I propose to unconditionally restart the timer when the timer message is generated. Are there any collateral effect?
--- server/queue.c.orig 2005-03-24 16:16:54.000000000 -0300 +++ server/queue.c 2005-04-13 16:10:42.000000000 -0300 @@ -972,7 +972,7 @@ if (win && timer->win != win) continue; if (timer->msg >= get_first && timer->msg <= get_last) { - if (remove) restart_timer( queue, timer ); + restart_timer( queue, timer ); return timer; } }
Jose Alonso alonso@estadao.com.br writes:
for (;;) {
(1) if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE )) ... { (2) DispatchMessageW( &msg ); } if (!fEndMenu) fRemove = TRUE;
/* finally remove message from the queue */ if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
(3) PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); else mt.trackFlags &= ~TF_SKIPREMOVE; }
(1) The WM_TIMER is peeked and the timer is not restarted (PM_NOREMOVE) (2) The WM_TIMER is dispatched here (3) The WM_TIMER is only removed from the queue. The next PeekMessageW (1) return immediately with WM_TIMER since the timer was not restarted.
The timer was restarted in (3), so I'd expect things to work right. There must be something else going on here.
On Thu, 2005-04-14 at 15:53 +0200, Alexandre Julliard wrote:
Jose Alonso alonso@estadao.com.br writes:
for (;;) {
(1) if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE )) ... { (2) DispatchMessageW( &msg ); } if (!fEndMenu) fRemove = TRUE;
/* finally remove message from the queue */ if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
(3) PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); else mt.trackFlags &= ~TF_SKIPREMOVE; }
(1) The WM_TIMER is peeked and the timer is not restarted (PM_NOREMOVE) (2) The WM_TIMER is dispatched here (3) The WM_TIMER is only removed from the queue. The next PeekMessageW (1) return immediately with WM_TIMER since the timer was not restarted.
The timer was restarted in (3), so I'd expect things to work right. There must be something else going on here.
The timer was not restarted in (3), because the message is removed in remove_queue_message.
DECL_HANDLER(get_message) ... if (get_posted_message( queue, ... ... get_posted_message ... remove_queue_message( queue, msg, POST_MESSAGE );
I think that another solution would be to check in the remove_queue_message if the message is WM_TIMER or WM_SYSTIMER and restart the corresponding timer.
alonso
Jose Alonso alonso@estadao.com.br writes:
The timer was not restarted in (3), because the message is removed in remove_queue_message.
DECL_HANDLER(get_message) ... if (get_posted_message( queue, ... ... get_posted_message ... remove_queue_message( queue, msg, POST_MESSAGE );
WM_TIMER messages are not posted, so the message can't possibly be removed by get_posted_message. It will be handled in find_expired_timer, which will return a new message and restart the timer.
On Thu, 2005-04-14 at 19:49 +0200, Alexandre Julliard wrote:
WM_TIMER messages are not posted, so the message can't possibly be removed by get_posted_message. It will be handled in find_expired_timer, which will return a new message and restart the timer.
Yes, the first time that PeekMessage (1) is called, the timer message is handled by find_expired_timer, but the timer is not restarted because the parameter PM_NOREMOVE is set. The WM_TIMER message remains in the queue, and next time the PeekMessage (3) is called with PM_REMOVE the get_post_message is called. I confirmed this by putting some "printf" in queue.c
alonso
Jose Alonso alonso@estadao.com.br writes:
Yes, the first time that PeekMessage (1) is called, the timer message is handled by find_expired_timer, but the timer is not restarted because the parameter PM_NOREMOVE is set. The WM_TIMER message remains in the queue, and next time the PeekMessage (3) is called with PM_REMOVE the get_post_message is called. I confirmed this by putting some "printf" in queue.c
The WM_TIMER cannot "remain in the queue", it's never put there in the first place, it's generated on the fly. If you really see a WM_TIMER in the posted message queue, it's because someone has done a PostMessage on it, and that would definitely be a bug. I doubt it's the problem you are seeing though.
On Thu, 2005-04-14 at 20:44 +0200, Alexandre Julliard wrote:
The WM_TIMER cannot "remain in the queue", it's never put there in the first place, it's generated on the fly. If you really see a WM_TIMER in the posted message queue, it's because someone has done a PostMessage on it, and that would definitely be a bug. I doubt it's the problem you are seeing though.
Ok. I made a wrong assumption. Continuing to investigate the problem, I found that: DispatchMessageW (2) is calling TimerProc and TimerProc is posting a WM_TIMER message. I am attaching a small program to reproduce this. If you click 'File' then happens the loop I mentioned.
I think one possible solution to avoid this case is the attached patch. (I think it's safe to remove unconditionally a message that will be dispatched).
alonso
Jose Alonso alonso@estadao.com.br writes:
I think one possible solution to avoid this case is the attached patch. (I think it's safe to remove unconditionally a message that will be dispatched).
Yes, that looks like a reasonable fix.