Calculate the Stack space
Local variables, including arrays, are allocated on the stack, which is generally _much_ smaller than the heap. A very large local array will cause the stack to collide with the heap, with a crash.
How much stack space can you safely use? The space available turns out to depend on the compiled application's Get Info memory allocation.
_max=31 LOCAL FN Test& DIM s$(_max) 'allocate (_max+1)*256 bytes + 12 bytes for a LOCAL FN END FN=FN STACKSPACE WINDOW 1 before& = FN STACKSPACE during& = FN Test& after& = FN STACKSPACE PRINT before& "before" PRINT during& "during" PRINT after& "after" PRINT "Used by FN Test&=" before&-during& DO UNTIL FN BUTTONInvestigation with a compiled app from the above program, and various memory allocations (in the OS 8.5 Get Info window) shows that FB2 sets the "before" stack as:-
initial_stack_size = min (32440, 1400 + 32*Kpartition)
where Kpartition is the number of K entered.
For example with 8000K the stack is a huge 257400 bytes. With 100K the stack is only 32440 bytes, and a crash should be expected if _max > = 126.
With a global DIM, the memory is taken from the application's heap.
I ran your program. Thanks.
I changed _max to 5000 and s$ to 100 chars. I ran it uncompiled in FBII with 18000K allocated. When I ran the modified code, during& went negative.
As I decrease the size of _max, during& approaches zero then goes positive.
What does that mean?
_max=5000 LOCAL FN Test& DIM 100 s$ (_max) END FN=FN STACKSPACE WINDOW 1 before& = FN STACKSPACE during& = FN Test& after& = FN STACKSPACE PRINT before& "before" PRINT during& "during" PRINT after& "after" PRINT "Used by FN Test&=" before&-during& DO UNTIL FN BUTTON
Pierre A. Zippi
When FN STACKSPACE is negative it means that you
(a) have attempted to use more stack than the system has reserved, and
(b) are a hair's-breadth away from a crash.
The only reason that your program didn't crash was that the array (illegally spilling over the top of the stack) was never accessed in FN Test&. The modified FN Test& below shows how to crash into BowelsOfTheMemoryManager.
Conclusion: don't use the stack for a large array. Either globally DIM it, or use relocatable memory (FN NEWPTR and XREF, or FN NEWHANDLE and XREF@).
_max=5000 LOCAL FN Test& DIM 100 s$ (_max) LONG IF FN STACKSPACE<100 INPUT "OK to crash?"; ok$ IF UCASE$(ok$)<>"Y" THEN END END IF FOR j=0 TO _max s$(j)="" NEXT END FN=FN STACKSPACE WINDOW 1 before& = FN STACKSPACE during& = FN Test& PRINT before& "before" PRINT during& "during" PRINT "Used by FN Test&=" before&-during& DO: UNTIL FN BUTTON
>> Conclusion: don't use the stack for a large array. Either globally DIM it, or <<
Now, would someone like to go out on a limb and say _how large_ is large? Is there some absolute number, or a proportion of total application size, or a proportion of the memory you've typically got available or something else entirely? I've only got one situation where I rely on local arrays in a big way, and have never had problems with it. But if there's a danger of collision (stacks? heaps? why can't I use scaffolding and re-bars?) I'd like to be sure I'm staying within the safety zone.
(1) This is "Black Art" stuff, but (3) and (4) below help.
(2) Every time a FN is called, its stack allocation is used. The allocation is released at the END FN statement.
(3) The total stack space is not documented in the FB manuals, but by experiment is:-
> initial_stack_size = min (32440, 1400 + 32*Kpartition)
> where Kpartition is the number of K entered
So in the main program of a compiled application you have at least 32K of stack. If the application's Get Info memory is greater than about 1000K, you'll have more.
(4) If stack overflow is a real worry, you can do a check within a FN, before allowing it to call another FN (or itself, as in this example)
LOCAL FN Recursion(depth) _myArrayBytes=1024 ' for DIM localStuff%(511) _myOtherVarBytes=100 ' guess (over-generous in this case) _safetyBytes@00 ' guess _stackBytesNeededForFN=_myArrayBytes_myOtherVarBytes_safetyBytes DIM localStuff%(511) 'use 1024 bytes of stack PRINT "Depth=" depth ; " Stack available=" FN STACKSPACE LONG IF FN STACKSPACE>_stackBytesNeededForFN 'ok for recursive call? FN Recursion(depth+1) XELSE PRINT "Maximum depth =" depth END IF END FN WINDOW 1 FN Recursion(0) DO: UNTIL FN BUTTONAs an illustration of Black Art aspect, in the FB environment I get a crash if _safetyBytes is less than about 4000, but in a compiled app _safetyBytes=1000 works OK.
(5) None of this messy stuff is necessary, unless you're allocating substantial chunks of LOCAL variable/array memory, or you're using deep recursion.