Remove recursion in getclosedlink

The function getclosedlink in report.c uses recursion to find closed links when reporting on disconnections.

In very large networks, it’s possible for the recursion to exhaust the memory on the call stack which then causes EPANET to crash.

If a loop is used instead of recursion, EPANET will not crash with very large disconnections
This commit is contained in:
Luke Butler
2023-09-14 16:00:34 -04:00
parent c84c6baee2
commit cfc06321a6
2 changed files with 37 additions and 14 deletions

View File

@@ -47,4 +47,5 @@ This document describes the changes and updates that have been made in version 2
- `EN_STATUS_REPORT` can now be used with `EN_getoption` and `EN_setoption` to get or set the type of status report that EPANET will generate (`EN_NO_REPORT`, `EN_NORMAL_REPORT` or `EN_FULL_REPORT`). - `EN_STATUS_REPORT` can now be used with `EN_getoption` and `EN_setoption` to get or set the type of status report that EPANET will generate (`EN_NO_REPORT`, `EN_NORMAL_REPORT` or `EN_FULL_REPORT`).
- A possible parser error that could result in a Trace Node ID in an input file not being recognized was fixed. - A possible parser error that could result in a Trace Node ID in an input file not being recognized was fixed.
- Additional API functions for enabling/disabling controls and rules were added. - Additional API functions for enabling/disabling controls and rules were added.
- Updated the internal function `getclosedlink` in report.c to use a loop instead of recursion to prevent a stack overflow during the analysis of very large disconnections.

View File

@@ -45,7 +45,7 @@ static void writeenergy(Project *);
static int writeresults(Project *); static int writeresults(Project *);
static int disconnected(Project *); static int disconnected(Project *);
static void marknodes(Project *, int, int *, char *); static void marknodes(Project *, int, int *, char *);
static void getclosedlink(Project *, int, char *); static void getclosedlink(Project *, int, char *, int *);
static void writelimits(Project *, int, int); static void writelimits(Project *, int, int);
static int checklimits(Report *, double *, int, int); static int checklimits(Report *, double *, int, int);
static char *fillstr(char *, char, int); static char *fillstr(char *, char, int);
@@ -1287,7 +1287,7 @@ int disconnected(Project *pr)
clocktime(rpt->Atime, time->Htime)); clocktime(rpt->Atime, time->Htime));
writeline(pr, pr->Msg); writeline(pr, pr->Msg);
} }
getclosedlink(pr, j, marked); getclosedlink(pr, j, marked, nodelist);
} }
// Free allocated memory // Free allocated memory
@@ -1350,11 +1350,12 @@ void marknodes(Project *pr, int m, int *nodelist, char *marked)
} }
} }
void getclosedlink(Project *pr, int i, char *marked) void getclosedlink(Project *pr, int i, char *marked, int *stack)
/* /*
**---------------------------------------------------------------- **----------------------------------------------------------------
** Input: i = junction index ** Input: i = junction index
** marked[] = marks nodes already examined ** marked[] = marks nodes already examined
** stack[] = stack to hold nodes to examine
** Output: None. ** Output: None.
** Purpose: Determines if a closed link connects to junction i. ** Purpose: Determines if a closed link connects to junction i.
**---------------------------------------------------------------- **----------------------------------------------------------------
@@ -1365,22 +1366,43 @@ void getclosedlink(Project *pr, int i, char *marked)
int j, k; int j, k;
Padjlist alink; Padjlist alink;
int top = 0;
// Mark the current junction as examined and push onto stack
marked[i] = 2; marked[i] = 2;
for (alink = net->Adjlist[i]; alink != NULL; alink = alink->next) stack[top] = i;
{
while (top >= 0) {
i = stack[top--];
alink = net->Adjlist[i];
// Iterate through each link adjacent to the current node
while (alink != NULL) {
k = alink->link; k = alink->link;
j = alink->node; j = alink->node;
if (marked[j] == 2) continue;
if (marked[j] == 1) // Skip nodes that have already been examined
{ if (marked[j] == 2) {
alink = alink->next;
continue;
}
// If a closed link is found, return and display a warning message
if (marked[j] == 1) {
sprintf(pr->Msg, WARN03c, net->Link[k].ID); sprintf(pr->Msg, WARN03c, net->Link[k].ID);
writeline(pr, pr->Msg); writeline(pr, pr->Msg);
return; return;
} }
else getclosedlink(pr, j, marked);
// Mark the node as examined and push it onto the stack
marked[j] = 2;
stack[++top] = j;
alink = alink->next;
} }
} }
}
void writelimits(Project *pr, int j1, int j2) void writelimits(Project *pr, int j1, int j2)
/* /*
**-------------------------------------------------------------- **--------------------------------------------------------------