Recursive function versus Recursive procedure

Posted by oedev on 05-Oct-2016 02:41

Currently in the process of trying to resolve an issue around a recursive function call in our code generating a stack overflow and core dump. In my investigation, it appears the converting the recursive function to a procedure fixes our issue. I can easily re-produce via an example to demonstrate a recursive function and procedure failing at different points with different errors.

Recursive function fails at 538 iterations with a core dump, recursive procedure at 19983 with error "SYSTEM ERROR: bfblst -- Too many block levels. (1)"

Starting my client with -s 200000 -nb 2000 as start-up parameters.

So these questions are;

1 - why the different points of failure and different errors

2 - Changing a function to a procedure and vice versa: can there be any side-effects, e.g. record scope etc.

Recursive procedure example;

OUTPUT TO VALUE("c:\temp\recursive-test-proc-output.txt") NO-ECHO.

PROCEDURE test:

    DEFINE INPUT PARAMETER Y AS INT.

    PUT UNFORMATTED Y SKIP.

    Y = Y + 1.

    RUN test(Y).

END PROCEDURE.


RUN test(0).



Recursive function example;

OUTPUT TO VALUE("c:\temp\recursive-test-func-output.txt") NO-ECHO.

FUNCTION test RETURNS LOGICAL (INPUT Y AS INT) :

    PUT UNFORMATTED Y SKIP.

    Y = Y + 1.

    test(Y).

END FUNCTION.


test(0).

All Replies

Posted by gus bjorklund on 05-Oct-2016 06:14

the function returns a value so it needs larger stack frames than the procedure.

Posted by oedev on 05-Oct-2016 06:18

Thanks for the reply Gus, which makes sense, but, the order of magnitude is surprising - nearly 4 fold increase in the number of iterations in this simple example.

Posted by oedev on 05-Oct-2016 06:24

And, actually if I add a output parameter to my procedure example, the point of failure remains the same.

DEFINE VARIABLE l AS LOGICAL     NO-UNDO.
OUTPUT TO VALUE("c:\temp\recursive-test-proc-output.txt") NO-ECHO.

PROCEDURE test:

    DEFINE INPUT PARAMETER Y AS INT.
    DEFINE OUTPUT PARAMETER l AS LOGICAL.

    PUT UNFORMATTED Y space(5) ETIME SKIP.

    Y = Y + 1.

    RUN test(Y, OUTPUT l).

END PROCEDURE.


RUN test(0, OUTPUT l).

Posted by Garry Hall on 05-Oct-2016 07:20

This comes down to the internal implementation of functions vs procedures in the AVM.Functions are pushed into the process stack, and I am guessing your recursive functions blew the process's stack space (different from the AVMs stack space, -s). If you posted the protrace for the recursive function crash, I would guess it shows this. Procedures are not executed the same way, and don't use up so much of the process's stack space. Instead they use the -s space.

Posted by oedev on 05-Oct-2016 08:52

Thanks for the explanation Garry. Is the process stack a fixed size and not changeable with parameters?

The protrace when the function fails looks like this;

=====================================================

PROGRESS stack trace as of Wed Oct 05 14:50:06 2016

=====================================================

Startup parameters:

-pf c:\oe102b\startup.pf,-cpinternal ISO8859-1,-cpstream ISO8859-1,-cpcoll Basic,-cpcase Basic,-d dmy,-numsep 44,-numdec 46,(end .pf),-s 200000,-nb 2000

Exception code: C00000FD STACK_OVERFLOW

Fault address:  101FDBE6 01:001FCBE6 C:\oe102b\bin\prow32.dll

Registers:

EAX:027F0EFC

EBX:00000000

ECX:00000000

EDX:00000003

ESI:00000000

EDI:027F0EFC

CS:EIP:0023:101FDBE6

SS:ESP:002B:00092A84  EBP:11953AFC

DS:002B  ES:002B  FS:0053  GS:002B

Flags:00010206

Posted by Laura Stern on 05-Oct-2016 09:07

As Garry implied, this is totally expected behavior.  Procedures and functions are not implemented the same way.  For Functions, we are looking at a Windows process stack size.  The AVM cannot change this.

Forgive me for asking, but why do you have so many levels of recursion.  It sounds like this is not a bug on your part, but intended behavior.  But it sounds kind of iffy to me!

Posted by Garry Hall on 05-Oct-2016 09:08

This is what I expected to see in the protrace.

On 32-bit Windows, I think the stack size is set during the compilation of the AVM executable, OE does not expose a way to reset the process's/thread's stack size at runtime. Depending on the OS, there might be changes you can make (a quick Google suggests ulimit), but I have not taken the time to investigate. You might also get different stack sizes on different platforms.

You are running something prior to 10.2B04. I haven't looked at the build parameters to know if we have increased the stack size in later versions.

Posted by oedev on 05-Oct-2016 10:17

Thanks both.

I've picked up another dev's code, not sure why they choose to use recursion for this particular implementation, definitely not essential. The easy refactoring seems to have been to change the function to a procedure and retain the recursion, for the moment.

Running 10.2B07.

Posted by marian.edu on 05-Oct-2016 10:52

Just for the sake of completeness... how things works for OO methods? Is a methods much like a function in that respect, if it’s a void one does it make it equivalent to a procedure or still seen as a function? Any differences between instance methods vs. static ones?


Thanks.

Marian Edu

Acorn IT 
+40 740 036 212

This thread is closed