[pypy-commit] pypy default: Make sys.setrecursionlimit() have an effect again: now, setting
arigo
noreply at buildbot.pypy.org
Fri May 27 11:22:27 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r44532:aadddddc702d
Date: 2011-05-26 23:33 +0200
http://bitbucket.org/pypy/pypy/changeset/aadddddc702d/
Log: Make sys.setrecursionlimit() have an effect again: now, setting it
to a value N sets the low-level maximum to N/1000 times 768KB.
Approximative but hopefully good enough.
diff --git a/pypy/jit/backend/llsupport/llmodel.py b/pypy/jit/backend/llsupport/llmodel.py
--- a/pypy/jit/backend/llsupport/llmodel.py
+++ b/pypy/jit/backend/llsupport/llmodel.py
@@ -144,10 +144,10 @@
lltype.Void))
def insert_stack_check():
startaddr = rstack._stack_get_start_adr()
- length = rstack._stack_get_length()
+ lengthaddr = rstack._stack_get_length_adr()
f = llhelper(STACK_CHECK_SLOWPATH, rstack.stack_check_slowpath)
slowpathaddr = rffi.cast(lltype.Signed, f)
- return startaddr, length, slowpathaddr
+ return startaddr, lengthaddr, slowpathaddr
self.pos_exception = pos_exception
self.pos_exc_value = pos_exc_value
diff --git a/pypy/jit/backend/x86/assembler.py b/pypy/jit/backend/x86/assembler.py
--- a/pypy/jit/backend/x86/assembler.py
+++ b/pypy/jit/backend/x86/assembler.py
@@ -621,10 +621,10 @@
if self.stack_check_slowpath == 0:
pass # no stack check (e.g. not translated)
else:
- startaddr, length, _ = self.cpu.insert_stack_check()
+ startaddr, lengthaddr, _ = self.cpu.insert_stack_check()
self.mc.MOV(eax, esp) # MOV eax, current
- self.mc.SUB(eax, heap(startaddr)) # SUB eax, [startaddr]
- self.mc.CMP(eax, imm(length)) # CMP eax, length
+ self.mc.SUB(eax, heap(startaddr)) # SUB eax, [start]
+ self.mc.CMP(eax, heap(lengthaddr)) # CMP eax, [length]
self.mc.J_il8(rx86.Conditions['B'], 0) # JB .skip
jb_location = self.mc.get_relative_pos()
self.mc.CALL(imm(self.stack_check_slowpath))# CALL slowpath
diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py
--- a/pypy/module/sys/vm.py
+++ b/pypy/module/sys/vm.py
@@ -43,21 +43,20 @@
return space.wrap(f)
def setrecursionlimit(space, w_new_limit):
- """setrecursionlimit() is ignored (and not needed) on PyPy.
-
-On CPython it would set the maximum number of nested calls that can
-occur before a RuntimeError is raised. On PyPy overflowing the stack
-also causes RuntimeErrors, but the limit is checked at a lower level.
-(The limit is currenty hard-coded at 768 KB, corresponding to roughly
-1480 Python calls on Linux.)"""
+ """setrecursionlimit() sets the maximum number of nested calls that
+can occur before a RuntimeError is raised. On PyPy the limit is
+approximative and checked at a lower level. The default 1000
+reserves 768KB of stack space, which should suffice (on Linux,
+depending on the compiler settings) for ~1400 calls. Setting the
+value to N reserves N/1000 times 768KB of stack space.
+"""
+ from pypy.rlib.rstack import _stack_set_length_fraction
new_limit = space.int_w(w_new_limit)
if new_limit <= 0:
raise OperationError(space.w_ValueError,
space.wrap("recursion limit must be positive"))
- # for now, don't rewrite a warning but silently ignore the
- # recursion limit.
- #space.warn('setrecursionlimit() is ignored (and not needed) on PyPy', space.w_RuntimeWarning)
space.sys.recursionlimit = new_limit
+ _stack_set_length_fraction(new_limit * 0.001)
def getrecursionlimit(space):
"""Return the last value set by setrecursionlimit().
diff --git a/pypy/rlib/rstack.py b/pypy/rlib/rstack.py
--- a/pypy/rlib/rstack.py
+++ b/pypy/rlib/rstack.py
@@ -46,11 +46,15 @@
lambda: 0)
_stack_get_length = llexternal('LL_stack_get_length', [], lltype.Signed,
lambda: 1)
+_stack_set_length_fraction = llexternal('LL_stack_set_length_fraction',
+ [lltype.Float], lltype.Void,
+ lambda frac: None)
_stack_too_big_slowpath = llexternal('LL_stack_too_big_slowpath',
[lltype.Signed], lltype.Char,
lambda cur: '\x00')
# the following is used by the JIT
_stack_get_start_adr = llexternal('LL_stack_get_start_adr', [], lltype.Signed)
+_stack_get_length_adr= llexternal('LL_stack_get_length_adr',[], lltype.Signed)
def stack_check():
diff --git a/pypy/translator/c/src/stack.h b/pypy/translator/c/src/stack.h
--- a/pypy/translator/c/src/stack.h
+++ b/pypy/translator/c/src/stack.h
@@ -12,14 +12,17 @@
#include "thread.h"
extern char *_LLstacktoobig_stack_start;
+extern long _LLstacktoobig_stack_length;
void LL_stack_unwind(void);
char LL_stack_too_big_slowpath(long); /* returns 0 (ok) or 1 (too big) */
+void LL_stack_set_length_fraction(double);
/* some macros referenced from pypy.rlib.rstack */
#define LL_stack_get_start() ((long)_LLstacktoobig_stack_start)
-#define LL_stack_get_length() MAX_STACK_SIZE
+#define LL_stack_get_length() _LLstacktoobig_stack_length
#define LL_stack_get_start_adr() ((long)&_LLstacktoobig_stack_start) /* JIT */
+#define LL_stack_get_length_adr() ((long)&_LLstacktoobig_stack_length)/* JIT */
#ifdef __GNUC__
@@ -51,12 +54,18 @@
}
char *_LLstacktoobig_stack_start = NULL;
+long _LLstacktoobig_stack_length = MAX_STACK_SIZE;
int stack_direction = 0;
RPyThreadStaticTLS start_tls_key;
+void LL_stack_set_length_fraction(double fraction)
+{
+ _LLstacktoobig_stack_length = (long)(MAX_STACK_SIZE * fraction);
+}
+
char LL_stack_too_big_slowpath(long current)
{
- long diff;
+ long diff, max_stack_size;
char *baseptr, *curptr = (char*)current;
/* The stack_start variable is updated to match the current value
@@ -83,22 +92,23 @@
}
baseptr = (char *) RPyThreadStaticTLS_Get(start_tls_key);
+ max_stack_size = _LLstacktoobig_stack_length;
if (baseptr != NULL) {
diff = curptr - baseptr;
- if (((unsigned long)diff) < (unsigned long)MAX_STACK_SIZE) {
+ if (((unsigned long)diff) < (unsigned long)max_stack_size) {
/* within bounds, probably just had a thread switch */
_LLstacktoobig_stack_start = baseptr;
return 0;
}
if (stack_direction > 0) {
- if (diff < 0 && diff > -MAX_STACK_SIZE)
+ if (diff < 0 && diff > -max_stack_size)
; /* stack underflow */
else
return 1; /* stack overflow (probably) */
}
else {
- if (diff >= MAX_STACK_SIZE && diff < 2*MAX_STACK_SIZE)
+ if (diff >= max_stack_size && diff < 2*max_stack_size)
; /* stack underflow */
else
return 1; /* stack overflow (probably) */
@@ -115,7 +125,7 @@
}
else {
/* the valid range is [curptr-MAX_STACK_SIZE+1:curptr+1] */
- baseptr = curptr - MAX_STACK_SIZE + 1;
+ baseptr = curptr - max_stack_size + 1;
}
RPyThreadStaticTLS_Set(start_tls_key, baseptr);
_LLstacktoobig_stack_start = baseptr;
diff --git a/pypy/translator/c/test/test_standalone.py b/pypy/translator/c/test/test_standalone.py
--- a/pypy/translator/c/test/test_standalone.py
+++ b/pypy/translator/c/test/test_standalone.py
@@ -689,6 +689,39 @@
out = cbuilder.cmdexec("")
assert out.strip() == "hi!"
+ def test_set_length_fraction(self):
+ # check for pypy.rlib.rstack._stack_set_length_fraction()
+ from pypy.rlib.rstack import _stack_set_length_fraction
+ from pypy.rlib.rstackovf import StackOverflow
+ class A:
+ n = 0
+ glob = A()
+ def f(n):
+ glob.n += 1
+ if n <= 0:
+ return 42
+ return f(n+1)
+ def entry_point(argv):
+ _stack_set_length_fraction(float(argv[1]))
+ try:
+ return f(1)
+ except StackOverflow:
+ print glob.n
+ return 0
+ t, cbuilder = self.compile(entry_point, stackcheck=True)
+ counts = {}
+ for fraction in [0.1, 0.4, 1.0]:
+ out = cbuilder.cmdexec(str(fraction))
+ print 'counts[%s]: %r' % (fraction, out)
+ counts[fraction] = int(out.strip())
+ #
+ assert counts[1.0] >= 1000
+ # ^^^ should actually be much more than 1000 for this small test
+ assert counts[0.1] < counts[0.4] / 3
+ assert counts[0.4] < counts[1.0] / 2
+ assert counts[0.1] > counts[0.4] / 7
+ assert counts[0.4] > counts[1.0] / 4
+
class TestMaemo(TestStandalone):
def setup_class(cls):
py.test.skip("TestMaemo: tests skipped for now")
More information about the pypy-commit
mailing list