prowin32 hungs unexpected

Posted by Stefan Marquardt on 21-Mar-2014 09:51

A little program is doing some mass updates and have, for monitoring, a short display procedure that is called at the end of a repeat block.
After running a short time and displaying 1500 it hungs, cpu uses 25 tp 29% (4 cores) and the attached debugger can't interrupt.
The workaround: Change 1500 to 100 so that this display with Process Events is called more often.

proc:

counter = counter + 1.

if counter mod 1500 = 0 then do:
            pause 0 before-hide.
            disp "Counter: " counter.
            process events.
end.

end proc.

Win 2008R2 64bit, OE 10.2B08 32bit

Any ideas except open a WR at PSC?

 

All Replies

Posted by James Palmer on 21-Mar-2014 10:16

Is this running in a GUI client?

Posted by Stefan Marquardt on 21-Mar-2014 10:17

It's running within the OE GUI: Standard Procedure Editor

Posted by James Palmer on 21-Mar-2014 10:20

Ah found what I was looking for.

PROCEDURE DisableProcessWindowsGhosting  EXTERNAL "USER32.dll":
END PROCEDURE.

Pop that in your code and run it before you run anything else. Does that help at all? Not sure if it's a problem in your OS, but it certainly helps this sort of issue in Win7.

Posted by Mike Fechner on 21-Mar-2014 10:23

http://knowledgebase.progress.com/articles/Article/000035144

 

Posted by Stefan Marquardt on 21-Mar-2014 10:27

I will try it, i forgot to write that it really hungs, not only thr gui.

promon shows one active transaction that doesn't changes, a few limbo locks and 0% I/O ...

Posted by Stefan Marquardt on 21-Mar-2014 10:51

The first test: It seems to work.

I don't understand why this helps.

I i run progetstack the last call is the procedure with the display ...

Is the gui blocking the application?

BTW: Nice KB, this function should be avoided :-)

Subsequently if a process is truly hung then this will affect application usability. In general, in ABL applications the DisableProcessWindowsGhosting function should be avoided if at all possible

Posted by Matt Gilarde on 21-Mar-2014 11:31

[quote user="Stefan Marquardt"]BTW: Nice KB, this function should be avoided :-)[/quote]

We don't recommend using DisableProcessWindowsGhosting because it makes it more difficult for users to kill the process if it hangs. Since you said that calling PROCESS EVENTS more often avoids the problem I would recommend that you take that approach rather than using DisableProcessWindowsGhosting.

Posted by James Palmer on 21-Mar-2014 11:39

Interesting you don't recommend it. What is your solution to ghosting then? If a query takes a certain amount of time to run then you get ghosting, and the users kill the process.

Posted by Stefan Drissen on 21-Mar-2014 11:41

[quote user="James Palmer"]

Interesting you don't recommend it. What is your solution to ghosting then? If a query takes a certain amount of time to run then you get ghosting, and the users kill the process.

[/quote]

Asynchronous AppServer calls.

Posted by James Palmer on 21-Mar-2014 11:44

Easier said than done in an application that's been around for 20 years. Yes I agree that's the ideal solution, but that takes time to implement. Time that certain managers see as unnecessary.

Posted by Matt Gilarde on 21-Mar-2014 11:50

[quote user="James Palmer"]Interesting you don't recommend it. What is your solution to ghosting then? If a query takes a certain amount of time to run then you get ghosting, and the users kill the process.[/quote]

Ghosting occurs when a process doesn't look at the message queue for more than five seconds. PROCESS EVENTS looks at the message queue, so calling it often enough avoids ghosting. I would only use DisableProcessWindowsGhosting if I couldn't use PROCESS EVENTS.

If you want to avoid PROCESS EVENTS you could call the PeekMessage API instead to let WIndows know that you're still alive. Here's an article about that approach (not OpenEdge-centric, but still applicable):

Telling Windows we’re not really “Not Responding”

DisableProcessWindowsGhosting would be my last resort.

Posted by Mike Fechner on 23-Mar-2014 14:10

Matt, do you volunteer to provide the ABL syntax to declare that API call?

Posted by Matt Gilarde on 23-Mar-2014 19:06

You successfully volunteered me. Here it is:


&SCOPED-DEFINE PM_NOREMOVE 0

PROCEDURE PeekMessageA EXTERNAL "user32.dll" :
    DEFINE INPUT  PARAMETER lpmsg         AS MEMPTR.
    DEFINE INPUT  PARAMETER hWnd          AS LONG.
    DEFINE INPUT  PARAMETER wMsgFilterMin AS LONG.
    DEFINE INPUT  PARAMETER wMsgFilterMax AS LONG.
    DEFINE INPUT  PARAMETER wRemoveMsg    AS LONG.
    DEFINE RETURN PARAMETER lResult       AS LONG.
END PROCEDURE.

DEFINE VARIABLE Msg AS MEMPTR.
DEFINE VARIABLE lResult AS INTEGER.

SET-SIZE(Msg) = 48. /* big enough for 64-bit */

/* This call to PeekMessage will not remove posted messages from the
** message queue. The one side effect I am aware of is that messages
** that were sent from another process using SendMessage will be
** dispatched before PeekMessage returns, but the same is true of
** any code that processes messages. Since you have to process
** messages to tell Windows you're awake, this seems like an
** unavoidable (and relatively rare) side effect.
*/
RUN PeekMessageA(Msg, 0, 0, 0, {&PM_NOREMOVE}, OUTPUT lResult).

Posted by Mike Fechner on 23-Mar-2014 23:30

:-)

Thanks!

Von meinem Windows Phone gesendet

Von: Matt Gilarde
Gesendet: ‎24.‎03.‎2014 01:07
An: TU.OE.Development@community.progress.com
Betreff: RE: prowin32 hungs unexpected

Reply by Matt Gilarde

You successfully volunteered me. Here it is:


&SCOPED-DEFINE PM_NOREMOVE 0

PROCEDURE PeekMessageA EXTERNAL "user32.dll" :
    DEFINE INPUT  PARAMETER lpmsg         AS MEMPTR.
    DEFINE INPUT  PARAMETER hWnd          AS LONG.
    DEFINE INPUT  PARAMETER wMsgFilterMin AS LONG.
    DEFINE INPUT  PARAMETER wMsgFilterMax AS LONG.
    DEFINE INPUT  PARAMETER wRemoveMsg    AS LONG.
    DEFINE RETURN PARAMETER lResult       AS LONG.
END PROCEDURE.

DEFINE VARIABLE Msg AS MEMPTR.
DEFINE VARIABLE lResult AS INTEGER.

SET-SIZE(Msg) = 48. /* big enough for 64-bit */

/* This call to PeekMessage will not remove posted messages from the
** message queue. The one side effect I am aware of is that messages
** that were sent from another process using SendMessage will be
** dispatched before PeekMessage returns, but the same is true of
** any code that processes messages. Since you have to process
** messages to tell Windows you're awake, this seems like an
** unavoidable (and relatively rare) side effect.
*/
RUN PeekMessageA(Msg, 0, 0, 0, {&PM_NOREMOVE}, OUTPUT lResult).

Stop receiving emails on this subject.

Flag this post as spam/abuse.

Posted by Stefan Marquardt on 24-Mar-2014 03:08

Hi Matt,

please notice that the application not only looks like that it's not responding, it really(!) never finish without using DisableProcessWindowsGhosting or more times PROCESS EVENTS.

Posted by Matt Gilarde on 24-Mar-2014 03:18

[quote user="Stefan Marquardt"]please notice that the application not only looks like that it's not responding, it really(!) never finish without using DisableProcessWindowsGhosting or more times PROCESS EVENTS.[/quote]PeekMessage(PM_NOREMOVE) is like a very lightweight version of PROCESS EVENTS. It checks whether there's anything in the message queue but doesn't have all of the overhead of PROCESS EVENTS and doesn't dispatch any messages like PROCESS EVENTS does. You can use PeekMessage in place of PROCESS EVENTS if all you need to do is keep the application responsive.

Posted by RWEBSTER on 24-Mar-2014 04:22

Matt, this is really good stuff, thanks!

The issue I see with both solutions is that the now 'alive but busy' window can't regain focus once it's lost, and so becomes 'trapped' behind other windows until the window finishes up. Is there anything that can be done here? I guess it may mean processing *some* of the messages, but not others? Thoughts?

Posted by Matt Gilarde on 24-Mar-2014 08:38

[quote user="RWEBSTER"]The issue I see with both solutions is that the now 'alive but busy' window can't regain focus once it's lost, and so becomes 'trapped' behind other windows until the window finishes up. Is there anything that can be done here? I guess it may mean processing *some* of the messages, but not others? Thoughts?[/quote]

You may be able to keep the busy window from becoming completely inactive by handling messages like WM_NCACTIVATE and the other WM_NC* messages. This is basically what SESSION:MULTITASKING-INTERVAL does. You can run the following code to see the difference. If you set MULTITASKING-INTERVAL to 0, you won't be able to bring the window to the foreground if you activate another application. If you set MULTITASKING-INTERVAL to 100 you will be able to bring the window to the foreground even though it's busy running the loop.


/* Set MULTITASKING-INTERVAL to a positive non-zero value to make
** the window respond to activation messages while it's busy. Set
** it to 0 to make the window not respond to activation messages.
*/

SESSION:MULTITASKING-INTERVAL = 100.

DEFINE VARIABLE i AS INTEGER.

DO i = 1 TO 1000000:
  DISPLAY i.
END.

MULTITASKING-INTERVAL uses PeekMessage to check the message queue but, unlike the PeekMessage code I posted earlier, it actually removes some messages and handles them. MULTITASKING-INTERVAL is sort of a lighter weight, automatic version of PROCESS EVENTS.

I realize that the more I say the more confusing this is probably becoming. I'm planning to create a page on the wiki to go into this subject in more depth. Each of the methods I've talked about (PROCESS EVENTS, MULTITASKING-INTERVAL, and PeekMessage) has its place and this topic deserves a fuller treatment than I have given in this piecemeal fashion.

Posted by Brian K. Maher on 24-Mar-2014 12:15

 
What about using SESSION:SET-WAIT-STATE("GENERAL") ??
 
 

Posted by Matt Gilarde on 24-Mar-2014 13:11

[quote user="Brian K. Maher"]What about using SESSION:SET-WAIT-STATE("GENERAL") ??[/quote]

SET-WAIT-STATE does two things - it disables an application's ABL windows and .NET forms so they don't accept input and it sets the mouse cursor to the hourglass. Other than those things, your program runs just as it would without SET-WAIT-STATE. It you go into the wait state and do something that takes a long time (a long loop, for example), you'll have the same issues as you would without entering the wait state.

Posted by Peter van Dam on 25-Mar-2014 11:38

I wonder if you good people on this thread have a solution for a somewhat related (or opposite?) issue: how to prevent the flickering when creating a dynamic dialog. Ever since the beginning of dynamic programming we have the problem that dynamic dialogs become visible too soon and therefore there is a lot of flickering going on when they are drawn. This problem has become bigger now with OpenEdge GUI for .NET when the ABL dialog is drawn over an embedded GUI window.

With windows we have no problem as we can control when they become visible. With dialogs however we have never been able to solve this.

I have tried LockWindowUpdate in the past, but that has the nasty side-effect of making the entire desktop flicker.

Are there similar solutions for this as discussed in this thread?

Posted by Peter van Dam on 26-Mar-2014 01:49

I guess my question is: is there an API call to temporarily prevent screen painting in an OpenEdge session?

Or any other trick to postpone realization of a dialog in OpenEdge?

Posted by Stefan Drissen on 26-Mar-2014 02:08

When we start creating widgets we disable redrawing on the window / dialog (SendMessageA, WM_SETREDRAW, value 0).

When finished we enable redrawing and force windows to RedrawWindow (RDW_INVALIDATE + RDW_ALLCHILDREN).

Posted by Peter van Dam on 26-Mar-2014 11:29

That sounds what I am looking for, I will give it a try. I wonder: don't you need to pass in the HWND or something like that?

Could you post some ABL sample code?

Posted by Stefan Drissen on 26-Mar-2014 16:14

Yes the first parameter of both APIs is HWND.

/* windows constants */

&GLOBAL-DEFINE WM_SETREDRAW        11
&GLOBAL-DEFINE RDW_INVALIDATE  0x0001
&GLOBAL-DEFINE RDW_ALLCHILDREN 0x0080

/* disable drawing */

RUN SendMessageA ( hw:HWND, {&WM_SETREDRAW}, 0, 0, OUTPUT idummy ).

/* enable drawing and force redraw */

RUN SendMessageA ( hw:HWND, {&WM_SETREDRAW}, 1, 0, OUTPUT idummy ).
RUN RedrawWindow ( hw:HWND, 0, 0, {&RDW_INVALIDATE} + {&RDW_ALLCHILDREN}, OUTPUT idummy )

/* external procedures */

PROCEDURE SendMessageA EXTERNAL "user32":u:
   DEFINE INPUT         PARAMETER i_hwnd    AS LONG NO-UNDO.
   DEFINE INPUT         PARAMETER i_imsg    AS LONG NO-UNDO.
   DEFINE INPUT         PARAMETER i_lwParam AS LONG NO-UNDO.
   DEFINE INPUT         PARAMETER i_lParam  AS LONG NO-UNDO.
   DEFINE       RETURN  PARAMETER lResult   AS LONG NO-UNDO.
END PROCEDURE.

PROCEDURE RedrawWindow  EXTERNAL "user32":u:
   DEFINE INPUT         PARAMETER i_hwnd       AS LONG NO-UNDO.
   DEFINE INPUT         PARAMETER i_lprcUpdate AS LONG NO-UNDO.
   DEFINE INPUT         PARAMETER i_hrgnUpdate AS LONG NO-UNDO.
   DEFINE INPUT         PARAMETER i_flags      AS LONG NO-UNDO.
   DEFINE       RETURN  PARAMETER o_lsuccess   AS LONG NO-UNDO.
END PROCEDURE.


Posted by Peter van Dam on 26-Mar-2014 18:15

Thank you very much Stefan, I going to dive into this!

-peter

Posted by Peter van Dam on 31-Mar-2014 07:37

Hi Stefan,

Unfortunately this does not work. The problem is that in the case of a dialog, the HWND property only becomes valid AFTER the dialog becomes visible :-(.

Or am I missing something?

-peter

Posted by Stefan Drissen on 01-Apr-2014 05:03

We create the window / dialog with visible, lock it, draw all widgets and then force the redraw. This does result in an empty window being shown, but prevents the flickerfest of widgets being added.

Posted by Stefan Drissen on 01-Apr-2014 05:09

And since this is an Advanced Business Language you can always play around with this nonsense:

DEF VAR hw     AS HANDLE   NO-UNDO.
DEF VAR ihwnd  AS INT64    NO-UNDO.


CREATE WINDOW hw ASSIGN
   X              = -10000
   Y              = -10000
   WIDTH-PIXELS   =  1
   HEIGHT-PIXELS  =  1
   VISIBLE        =  TRUE
   .

ASSIGN
   ihwnd = hw:HWND
   hw:VISIBLE = FALSE
   .

MESSAGE ihwnd VIEW-AS ALERT-BOX.

ASSIGN 
   hw:X              =  100
   hw:Y              =  100
   hw:WIDTH-PIXELS   =  200
   hw:HEIGHT-PIXELS  =  200
   hw:VISIBLE        =  TRUE 
   .
WAIT-FOR CLOSE OF hw.


Posted by Peter van Dam on 01-Apr-2014 07:45

That is a jolly good idea. Let me play around with it.

Posted by Peter Judge on 01-Apr-2014 08:54

How does the below work for you?
WAIT-FOR CLOSE, WINDOW-CLOSE OF hw.
 
 
-- peter
 
[collapse]
From: Stefan Drissen [mailto:bounce-14941@community.progress.com]
Sent: Tuesday, 01 April, 2014 06:10
To: TU.OE.Development@community.progress.com
Subject: RE: prowin32 hungs unexpected
 
Reply by Stefan Drissen

And since this is an Advanced Business Language you can always play around with this nonsense:

DEF VAR hw     AS HANDLE   NO-UNDO.
DEF VAR ihwnd  AS INT64    NO-UNDO.
 
 
CREATE WINDOW hw ASSIGN
   X              = -10000
   Y              = -10000
   WIDTH-PIXELS   =  1
   HEIGHT-PIXELS  =  1
   VISIBLE        =  TRUE
   .
 
ASSIGN
   ihwnd = hw:HWND
   hw:VISIBLE = FALSE
   .
 
MESSAGE ihwnd VIEW-AS ALERT-BOX.
 
ASSIGN 
   hw:X              =  100
   hw:Y              =  100
   hw:WIDTH-PIXELS   =  200
   hw:HEIGHT-PIXELS  =  200
   hw:VISIBLE        =  TRUE 
   .
WAIT-FOR CLOSE OF hw.

 

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by Stefan Marquardt on 30-Oct-2014 10:25

I stlil have the unsolved problem and more and more users complains.

This problem was introduced after moving from 10.2A to 10.2B08 and Win2003 to Win2008R2 on the citrix farm.

I have this problem running a little code in the GUI editor which updates in a loop a bunch of records. (direct on the server where the database reside)

I press "run", don't "touch" the window and it never comes back and hangup with 100% cpu use.

We can't change every line of code to avoid mass updates without calling Process events or other proposals.
It should work without a code change like before.

Posted by TheMadDBA on 30-Oct-2014 10:33

Stefan: Does this only happen on Citrix or does it happen on local desktops as well?

Posted by Stefan Marquardt on 30-Oct-2014 10:42

Hi, it happens on the server (local via rdp) too.

Posted by TheMadDBA on 30-Oct-2014 10:51

Have you tried just using Progress locally and connecting to the DB client server and see if it still happens? The reason I ask is there are several issues with Citrix/Terminal Server.

But if it happens on a normal windows desktop it could be other issues.

This thread is closed