[comp.sys.amiga] Dynamic Stack Allocation

c164-1bj@cordelia.berkeley.edu (Jonathan Dubman) (10/26/87)

I have a speedy maze program I wrote in C that I want to send to Fred and post
here, but it needs a large stack.  Maximum resolution requires about 150K of
stack space. (Massive recursion.)  I don't want everybody who runs it without
reading the docs first to crash their machine.

Is it possible in a C program to know how much stack space I have?  I
can write a short assembly language program if necessary.

Is it possible, if the stack is too small, to dynamically allocate stack
space?  Can I just allocate a big chunk of memory, reset the stack pointer
to there, and deallocate it at the end, resetting the stack pointer to the
original stack that never went away?

The least I can do is say, "Reset your stack size and run me again.".
If I run it from workbench, WB will allocate the stack of the proper size,
provided the .info file is correct.  CLI is the problem.

	*&Jonathan Dubman

bryce@hoser.berkeley.edu (Bryce Nesbitt) (10/26/87)

In article <4585@zen.berkeley.edu> c164-1bj@cordelia.berkeley.edu (Jonathan Dubman) writes:
>I have a speedy maze program I wrote in C that I want to send to Fred and post
>here, but it needs a large stack.  Maximum resolution requires about 150K of
>stack space. (Massive recursion.)  I don't want everybody who runs it without
>reading the docs first to crash their machine.
>
>Is it possible in a C program to know how much stack space I have?  I
>can write a short assembly language program if necessary.

From C, simply get a pointer to your task with "FindTask(0L);" This returns
a pointer to struct Task from the exec/tasks.h include file.  tc_SPLower is
the lower bound of the stack tc_SPupper is the upper bound +2.

It would be thoretically possible to allocate a new stack and start to use
it.  This sounds somewhat dangerous, however.  There is no official function
set up to swap stacks (and the requisite Task structure fields).  In the
future, particuarly the resource tracked future, you could be asking for
problems.  At least you know it is safe to:

>say, "Reset your stack size and run me again.".

Anyone starting it from Workbench will only get that message if they played
with the icon.  Anyone starting from the CLI when an icon exists can probably
figure out how to type "stack 150000".

Sounds like your program should also leave the automatic stack checking code
in.  This will pop a "Stack overflow" requester if a function call will
overflow.  You *must*, however, catch the exit and do a proper cleanup.

|\ /|  . Ack! (NAK, SOH, EOT)
{o O} . bryce@hoser.berkeley.EDU -or- ucbvax!hoser!bryce
 (")
  U	"Here. This'll shoot the lips off a cockroch" -Freedom Fighter	

ali@rocky.STANFORD.EDU (Ali Ozer) (10/26/87)

In article <21440@ucbvax.BERKELEY.EDU> Bryce Nesbitt writes:
>In article <4585@zen.berkeley.edu> Jonathan Dubman writes:
>>I have a speedy maze program ...
>>it needs a large stack.  Maximum resolution requires about 150K of
>>stack space. (Massive recursion.)   ...
>> "Reset your stack size and run me again.".
>
>Anyone starting it from Workbench will only get that message if they played
>with the icon.  Anyone starting from the CLI when an icon exists can probably
>figure out how to type "stack 150000".

You might also try launching a subprocess with the correct
stack size. In fact, you might be able to determine the necessary stack
size from the resolution and launch the main recursize code with the 
stack size set "just right." If you can prevent the recursive section of the
code from doing any DOS stuff (like file I/O), then you can make it
a task, and include it as a function in your program. Then, to launch
it you would use CreateTask(), which takes a stack size and a
starting address as an argument. If it somehow fails, it could send back
a message or a signal to the parent task. This structure could also make
it possible to abort the computations more gracefully, perhaps, if you
had the parent waiting on both the child's and Intuition's signals.
If the child did no memory allocations of its own, then the parent could
kill it will a single RemTask(), >poof<.

Ali Ozer, ali@rocky.stanford.edu

dillon@CORY.BERKELEY.EDU (Matt Dillon) (10/27/87)

>I have a speedy maze program I wrote in C that I want to send to Fred and post
>here, but it needs a large stack.  Maximum resolution requires about 150K of
>stack space. (Massive recursion.)  I don't want everybody who runs it without
>reading the docs first to crash their machine.

	A program which uses that much stack should not assume it will be
given it on startup.  All you really have to do is allocate enough memory
for your stack, then set your SP to the end of it.  Be sure to set the SP
back to the original stack and free the allocated memory before exiting.
Remember that at any time an interrupt can come in and push+pop stuff on
the stack.  I'd say write a small assembly routine which 'switches' stacks...

massiverecursion:
   	(allocate new stack)
	move.l  sp,A0
	move.l  EndofNewstack,sp
	move.l  A0,-(sp)
	jsr     routine that requires massive recursion
	move.l  (sp)+,sp
	(deallocate new stack)
	rts

				-Matt

ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) (10/27/87)

In article <21440@ucbvax.BERKELEY.EDU> bryce@hoser.berkeley.edu (Bryce Nesbitt) writes:
>In article <4585@zen.berkeley.edu> c164-1bj@cordelia.berkeley.edu (Jonathan Dubman) writes:
>>Is it possible in a C program to know how much stack space I have?  I
>>can write a short assembly language program if necessary.
>
>From C, simply get a pointer to your task with "FindTask(0L);" This returns
>a pointer to struct Task from the exec/tasks.h include file.  tc_SPLower is
>the lower bound of the stack tc_SPupper is the upper bound +2.
>
	Gee Byrce, I'm surprised you missed this.

	Yes, there are tc_SP{Upper,Lower} fields in the task control block,
but these are not what DOS uses for the stack.  That's stored in your
process structure (an extension of Exec's task structure).

	To find out the size of the stack DOS allocated for you when your
program was started, you say:

	stacksize = ((struct Process *) FindTask (0L)) -> pr_StackSize;

	I found this out a while ago when trying to debug a program.  For
some strange reason I decided to check to see if the stack had a sane value.
Much to my consternation, I found the stack pointer pointing completely
outside the range specified by tc_SP{Upper,Lower}.  It suddenly dawned on me
that DOS was probably screwing this up, too, and poked around in the process
structure.

	Hope this helps.  (Hope I'm right about it, too!)

_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
Leo L. Schwab -- The Guy in The Cape	ihnp4!ptsfa -\
 \_ -_		Recumbent Bikes:	      dual ---> !{well,unicom}!ewhac
O----^o	      The Only Way To Fly.	      hplabs / (pronounced "AE-wack")
"Although there are technical differences between the quality of images
created on the Amiga and on our system, we feel that viewers could be misled
to believe otherwise, even with your disclaimers to the contrary."
		-- Ralph J. Guggenheim, Pixar

bryce@hoser.berkeley.edu.UUCP (10/28/87)

ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes:
>bryce@hoser.berkeley.edu (Bryce Nesbitt) writes:
>
>> From C, simply get a pointer to your task with "FindTask(0L);" This returns
>> a pointer to struct Task from the exec/tasks.h include file.  tc_SPLower is
>> the lower bound of the stack tc_SPupper is the upper bound +2.
>
> Gee Byrce, I'm surprised you missed this.
> Yes, there are tc_SP{Upper,Lower} fields in the task control block,
> but these are not what DOS uses for the stack...
> ...To find out the size of the stack DOS allocated for you when your
> program was started, you say:
>	stacksize = ((struct Process *) FindTask (0L)) -> pr_StackSize;


Guess what Leo?  We are _both_ wrong!  So much for Samaritanism.

Actually we are both right.  Got that one figured out yet?


There is a not_so_subtle difference between the Workbench and the CLI.
pr_StackSize GIVES THE WRONG ANSWER!!  Better yet, pr_StackSize and
cli_DefaultStack, both declared as longs, are stored in different units!

I was so confused about the issue Leo brought up that only a sample
program saved me.  Try it from Workbench, "run" and direct CLI.  Set
a new stack number before each trial.

Take note of just who's stack pr_StackSize represents (Hint: It's not always
yours).  Take note that my solution will not work either.

(Also be on the lookout for a certain piece of commercial Amiga software that,
when run from the CLI, seems to use 8,000 more bytes than it needs to...
then complain to the programmer. :-)

/*
 * stack_test.c  27-Oct-87.  Bryce Nesbitt
 *
 * Created so I could have a clue.  Just how is that stacksize stuff saved?
 *
 * This program points out that CLI started programs will have the stacksize
 * from cli_DefaultStack.  This size is also saved at 4(a7) at startup... but
 * that is not available to a C program.  THE NORMAL TASK STRUCTURE REFERS TO
 * THE CLI PROCESSES' STACK... NOT THE ONE YOU ARE RUNNING ON!
 *
 * From the Workbench there is no cli_DefaultStack.  Here the normal task
 * structures take hold.
 *
 */
#include "exec/types.h"
#include "exec/tasks.h"
#include "libraries/dosextens.h"
#include "stdio.h"
struct Process *FindTask();

main()
{
register struct Process     *Process;
register FILE		    *Handle;
struct CommandLineInterface *CLI;

    if (!(Handle=fopen("con:0/11/250/128/Stack Window","a")))
	exit(20);	/* "a" is used so the window won't flicker */

    Process=FindTask(0L);

    if (CLI=(struct CommandLineInterface *)(Process->pr_CLI<<2))
	{
	if (CLI->cli_Background)
	    fprintf(Handle,"Background");
	else
	    fprintf(Handle,"Foreground");
	fprintf(Handle," CLI #%ld\n\n",Process->pr_TaskNum);

	fprintf(Handle,"cli_DefaultStack is: %ld\n\n",\
	       CLI->cli_DefaultStack<<2);
	}
    else
	{
	fprintf(Handle,"This is not a CLI process\n");
	fprintf(Handle,"Stack is %ld\n\n",Process->pr_StackSize);
	}

    /* Other useless information (we already know the stack size) */
    fprintf(Handle,"pr_StackSize     %ld\n",Process->pr_StackSize);
    fprintf(Handle,"pr_StackBase     $%lx\n\n",Process->pr_StackBase<<2);
    fprintf(Handle,"tc_SPLower       $%lx\n",Process->pr_Task.tc_SPLower);
    fprintf(Handle,"tc_SPUpper       $%lx\n",Process->pr_Task.tc_SPUpper);
    fprintf(Handle,"Upper-Lower      %ld\n\n",
	   (long)Process->pr_Task.tc_SPUpper-\
	   (long)Process->pr_Task.tc_SPLower);
    fprintf(Handle,"tc_SPReg         $%lx\n",Process->pr_Task.tc_SPReg);

    Delay(75L);     /* Be quick :-) */
    fprintf(Handle,"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
		    /* Dramatic exit :-) */
    fclose(Handle);
}


|\ /|  . Ack! (NAK, SOH, EOT)
{o O} . bryce@hoser.berkeley.EDU -or- ucbvax!hoser!bryce
 (")
  U	"I never said you _could_, I merely said you _should_." -Humpty
	Dumpty	

karl@sugar.UUCP (11/01/87)

In article <4585@zen.berkeley.edu>, c164-1bj@cordelia.berkeley.edu (Jonathan Dubman) writes:
> .... it needs a large stack.  Maximum resolution requires about 150K of
> stack space. (Massive recursion.)  I don't want everybody who runs it without
> reading the docs first to crash their machine.

Most of the responses so far have been hacks for increasing the amount of
stack space.  Why not malloc the space and handle your recursive data 
yourself, managing your data frames explicitly by incrementing and 
decrementing an index on entry and exit of your recursive routine?  
(With this method, by the way, one can write recursive programs in BASIC.)  
Note that I'm assuming you're putting pretty much stuff on the stack each pass.
If you needed 150K depth for a little frame but super deep recursion,
this wouldn't gain much, if anything.
--