Consultor Eletrônico



Kbase P93187: Infinite loop with toolbar delete and create buttons flashing when deleting a last record in a Brows
Autor   Progress Software Corporation - Progress
Acesso   Público
Publicação   12/01/2005
Status: Unverified

FACT(s) (Environment):

Dynamics 2.1A

SYMPTOM(s):

Dynamics 2.1A02

Deleting the last record in a child SDO (only one record)

Infinite loop with the delete button and the create button flashing in the toolbar (enabled and disabled)

ProSpy shows a trace with infinite loop on ROW-ENTRY and OFF-END browse triggers

CAUSE:

Known issue, development aware. In adm2/browser.p/enableFields, there is an unconditional APPLY ENTRY in hBrowse that can make serious problems when deleting a last record in an updatable browse. In some cases, this can lead to an infinite loop situation with create and delete buttons flashing (switching from enabled to disabled forever), which can only be stopped by pressing ctrl-break.

This is due to a behavior at the core level: when the browse is empty (just deleted the last record), then doing an APPLY ENTRY fires the OFF-END trigger even when the focus is already in the browse (observed also with pure 4GL).

More details about the infinite loop: at some point, adm2/browser.p/enableField does an unconditional APPLY ENTRY to hBrowse even when it is empty (it has no row). This fires the OFF-END triggers even if the focus is already in the browse. This then fires browser.p/offEnd that will try to fetch a new batch (but there is no more records) that will run updateMode that will eventually run enableFields again. Note: to see the real 4GL stack trace when OFF-END is fired, implement a ENTRY trigger in rydynbrowb.w with a message, then choose help to see the stack trace.

FIX:

Solution is to modify adm2/browser.p/enableFields do the APPLY ENTRY only if the focus is not already in the browse (in order to not fire OFF-END when not required), and when this browse is not empty. To do so, implement the following code in adm2/browser.p/enableFields (see the tags "slacroix 01-sep-2004"):
PROCEDURE enableFields

/*------------------------------------------------------------------------------
Purpose: Makes the Browse control updatable.
Parameters:
Notes: The Browse is made updatable only if some field are enabled
and the SmartDataBrowser is link to an Update target.
------------------------------------------------------------------------------*/

DEFINE VARIABLE lSelected AS LOGICAL NO-UNDO.
DEFINE VARIABLE hBrowse AS HANDLE NO-UNDO.
DEFINE VARIABLE hFrame AS HANDLE NO-UNDO.
DEFINE VARIABLE hColumn AS HANDLE NO-UNDO.
DEFINE VARIABLE lEnabled AS LOGICAL NO-UNDO.
DEFINE VARIABLE cDisplayedFields AS CHARACTER NO-UNDO.
DEFINE VARIABLE cFields AS CHARACTER NO-UNDO.
DEFINE VARIABLE cFieldSecurity AS CHARACTER NO-UNDO.
DEFINE VARIABLE cState AS CHARACTER NO-UNDO.
DEFINE VARIABLE cTarget AS CHARACTER NO-UNDO.
DEFINE VARIABLE iFieldPos AS INTEGER NO-UNDO.

{get BrowseHandle hBrowse}.
IF VALID-HANDLE(hBrowse) THEN
DO:
&SCOPED-DEFINE xp-assign
{get EnabledFields cFields}
{get FieldsEnabled lEnabled}
{get RecordState cState}
{get UpdateTarget cTarget}
{get DisplayedFields cDisplayedFields}
{get FieldSecurity cFieldSecurity}.
&UNDEFINE xp-assign

IF cFields NE "":U AND (NOT lEnabled) /* There are fields to enable */
AND cTarget NE "":U /* and an Update-Target */
THEN DO:
lSelected = hBrowse:NUM-SELECTED-ROWS = 1. /* Was a row selected? */
{get ContainerHandle hFrame}.

APPLY "ENTRY":U TO hFrame.

/* Turn off read-only for each column
See comments in disable fields why read-only is true */
hColumn = hBrowse:FIRST-COLUMN.
DO WHILE VALID-HANDLE(hColumn):
iFieldPos = LOOKUP(hColumn:NAME,cDisplayedFields).
IF CAN-DO(cFields,hColumn:NAME) AND
((iFieldPos <> 0 AND
NUM-ENTRIES(cFieldSecurity) >= iFieldPos AND
ENTRY(iFieldPos,cFieldSecurity) = "":U) OR
iFieldPos = 0 OR
cFieldSecurity = "":U) THEN
hColumn:READ-ONLY = FALSE.
hColumn = hColumn:NEXT-COLUMN.
END.
hBrowse:READ-ONLY = no.
hBrowse:SELECT-FOCUSED-ROW() NO-ERROR.

/* slacroix 01-sep-2004 begin, why putting the focus in the browse if
no record in it? It can lead to an infinite loop when deleting last record
in an updatable browse with OFF-END that fires when the focus goes in the
empty browse, which fires this procedure to enable the field
so I make this apply entry conditional */
IF lSelected
AND FOCUS <> hBrowse THEN /* slacroix 01-sep-2004 end*/
APPLY "ENTRY":U TO hBrowse.

{set FieldsEnabled yes}.
END.


IF cState = 'NoRecordAvailable':U THEN DO:
{get ContainerHandle hFrame}.
APPLY "ENTRY":U TO hFrame.
END. /* if no record available */
END.
RUN SUPER.

RETURN.

END PROCEDURE.