[Python-Dev] stack check on Unix: any suggestions?

Thomas Wouters thomas@xs4all.net
Tue, 29 Aug 2000 21:49:12 +0200


On Tue, Aug 29, 2000 at 02:42:41PM -0400, Jeremy Hylton wrote:

> Is there any way to implement PyOS_CheckStack on Unix?  I imagine that
> each platform would have its own variant and that there is no hope of
> getting them debugged before 2.0b1.

I can think of three mechanisms: using getrusage() and getrlimit() to find out
the current stacksize and the stack limit is most likely to give accurate
numbers, but is only available on most UNIX systems, not all of them. (I
hear there are systems that don't implement getrusage/getrlimit ;-)

int PyOS_CheckStack(void)
{
    struct rlimit rlim;
    struct rusage rusage;

    if (getrusage(RUSAGE_SELF, &rusage) != 0)
        /* getrusage failed, ignore or raise error ? */
    if (getrlimit(RLIMIT_STACK, &rlim) != 0)
        /* ditto */
    return rlim.rlim_cur > rusage.ru_isrss + PYOS_STACK_MARGIN;
}

(Note that it's probably necessary to repeat the getrlimit as well as the
getrusage, because even the 'hard' limit can change -- a Python program can
change the limits using the 'resource' module.) There are currently no
autoconf checks for rlimit/rusage, but we can add those without problem.
(and enable the resource automagically module while we're at it ;)

If that fails, I don't think there is a way to get the stack limit (unless
it's in platform-dependant ways) but there might be a way to get the
approximate size of the stack by comparing the address of a local variable
with the stored address of a local variable set at the start of Python.
Something like

static long stack_start_addr

[... in some init function ...]
    int dummy;
    stack_start_addr = (long) &dummy;
[ or better yet, use a real variable from that function, but one that won't
get optimized away (or you might lose that optimization) ]

#define PY_STACK_LIMIT 0x200000 /* 2Mbyte */

int PyOS_CheckStack(void)
{
    int dummy;
    return abs(stack_start_addr - (long)&dummy) < PY_STACK_LIMIT;
}

This is definately sub-optimal! With the fixed stack-limit, which might both
be too high and too low. Note that the abs() is necessary to accomodate both
stacks that grow downwards and those that grow upwards, though I'm
hard-pressed at the moment to name a UNIX system with an upwards growing
stack. And this solution is likely to get bitten in the unshapely behind by
optimizing, too-smart-for-their-own-good compilers, possibly requiring a
'volatile' qualifier to make them keep their hands off of it.

But the final solution, using alloca() like the Windows check does, is even
less portable... alloca() isn't available on some systems (more than
getrlimit isn't available on, I think, but the two sets are likely to
intersect) and I've heard rumours that on some systems it's even an alias
for malloc(), leading to memory leaks and other weird behaviour.

I'm thinking that a combination of #1 and #2 is best, where #1 is used when
getrlimit/getrusage are available, but #2 if they are not. However, I'm not
sure if either works, so it's a bit soon for that kind of thoughts :-)

> Does stackless raise exceptions cleanly on each of these bugs?  That
> would be an argument worth mentioning in the PEP, eh?

No, I don't think it does. Stackless gets bitten much later by recursive
behaviour, though, and just retains the current 'recursion depth' counter,
possibly set a bit higher. (I'm not sure, but I'm sure a true stackophobe
will clarify ;)

-- 
Thomas Wouters <thomas@xs4all.net>

Hi! I'm a .signature virus! copy me into your .signature file to help me spread!