Event Procedure Diagnostic Report

After you have finished executing your application, examine the resulting report as follows:

Search for warnings
After your program finishes, examine the error file and search for lines with the following format:
warning: procedure for event type {a} ran for {b} seconds (address {c} in {d})

Where the parameters are:

{a}
The numeric value of the event type that triggered the long-running Event Procedure.
{b}
The number of seconds the Event Procedure actually ran.
{c}
The address of the Event Procedure.
{d}
The PROGRAM-ID of the program containing the Event Procedure.
Run the built-in run-time debugger
To help you understand the event type {a} and the Event Procedure address {c}, run the application using the built-in run-time debugger with the -d parameter.

If the program you are debugging is your initial program, you do not need to set a breakpoint because you are already where you want to be. However, if the affected program is not the initial program, set a breakpoint at the beginning of the program with:

B .0,PROGRAM-ID

Where PROGRAM-ID is the ID of your program.

Run the program until you hit that breakpoint.

Note: Every program, except for the initial one, always executes address ".0" as its first step, so this is a reliable way to break at the beginning of a program.

Now you can look up the event type by locating the acugui.def COPY file. This is normally be included in the program, so you can just use the ft acugui.def command. From here you can scan or search for the event type number, for example, f 16413 leads to the line that corresponds to Msg-Tv-Expanding, which shows you the name of the event type.

To find the Event Procedure, you can use the command w followed by the address {c}. This centers the screen at the start of the Event Procedure. For example, if the reported address is .0001A9, you can simply type w .1a9.

From this point you can set further breakpoints and perform normal debugging, though often it is simply adequate to read the Event Procedure to see what it is doing.

For more information on using the built-in run-time debugger, see Run-Time Debugger.

What to look for
You are looking for an explanation as to why the Event Procedure took as long to run as it did. At this point, some judgement is required. Some cases are legitimate, and some are problematic.
Legitimate cases
When the long-running code must execute before the UI can continue, this is a legitimate case. The following are examples of legitimate cases:
  • The control needs a SORT of a large amount of data in direct response to the event.
  • The user must enter a password to continue.
  • Something occurred to cause a message box to pop-up and the user is slow to respond. Maybe they went to lunch?
  • The control launches a lookup search and needs the response to continue. This is fine providing the search is modal, meaning that you can't use the rest of the UI until the search box is closed.

Basically, any case where the control itself needs the results of the Event Procedure is legitimate, providing the remainder of the UI is kept inert. Subsidiary controls such as password boxes, search boxes, and message boxes can run, but you should avoid this when the rest of the screen is being kept alive during the Event Procedure.

Problematic cases
Problematic cases are most often embedded CALL or PERFORM statements that lead to a parallel use of the full UI. For example, something that spawns a window containing a copy of the current UI or leads to the full application menu. These are trouble because the application lives on under the current event without letting the event ever finish. They can be hard to spot, for example, if the CALL statement is to some general handler that serves multiple functions, one of which is to spawn a parallel run of the application.
Remedies
After examination, you may decide that you want to restructure the Event Procedure to reduce its long run time. Here are a couple of easy ways to do that.
Using threads
Quite often, the easiest thing to do is to create a thread to run the long procedure. This can be something as simple as replacing CALL procedure with CALL THREAD procedure. Alternatively you can do something like this:
PERFORM THREAD
    <long-running code>
END-PERFORM

Starting a new thread to handle the long-running code allows the Event Procedure itself to finish quickly, and allows control to return to the UI while the long-running code runs in parallel. This remedy is usually appropriate as long as the control generating the event does not directly rely on the information returned by the Event Procedure.

Using exceptions
Another approach is to turn the long-running code into an Exception Procedure and then use the Event Procedure to generate an exception instead of executing the code directly. This works because Exception Procedures are not run as children of an ACCEPT, but instead are run as siblings. The flow of control is that the Exception Procedure flags that it wants an exception to occur and then returns to the UI, which then returns to the runtime with an exception.

To generate an exception, you can use the EVENT-ACTION field of the EVENT-STATUS item defined in crtvars.def. By setting this to EVENT-ACTION-TERMINATE in the Event Procedure, the UI terminates with a CRT STATUS value of W-EVENT, generating an exception state.