Skip to content

Commit 19ac31a

Browse files
committed
Make per-thread data-stack a contiguous block of memory.
1 parent 24a77d8 commit 19ac31a

File tree

5 files changed

+55
-10
lines changed

5 files changed

+55
-10
lines changed

Include/cpython/pystate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ struct _ts {
149149

150150
CFrame root_cframe;
151151

152+
PyObject **datastack_base;
153+
PyObject **datastack_top;
154+
PyObject **datastack_soft_limit;
155+
PyObject **datastack_hard_limit;
152156
/* XXX signal handlers should also be here */
153157

154158
};

Include/internal/pycore_pymem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ struct _PyTraceMalloc_Config {
9595
PyAPI_DATA(struct _PyTraceMalloc_Config) _Py_tracemalloc_config;
9696

9797

98+
void *_PyObject_VirtualAlloc(size_t size);
99+
void _PyObject_VirtualFree(void *, size_t size);
100+
101+
98102
#ifdef __cplusplus
99103
}
100104
#endif

Objects/obmalloc.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,18 @@ PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)
552552
*allocator = _PyObject_Arena;
553553
}
554554

555+
void *
556+
_PyObject_VirtualAlloc(size_t size)
557+
{
558+
return _PyObject_Arena.alloc(_PyObject_Arena.ctx, size);
559+
}
560+
561+
void
562+
_PyObject_VirtualFree(void *obj, size_t size)
563+
{
564+
return _PyObject_Arena.free(_PyObject_Arena.ctx, obj, size);
565+
}
566+
555567
void
556568
PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)
557569
{
@@ -3035,7 +3047,7 @@ _PyObject_DebugMallocStats(FILE *out)
30353047

30363048
fputc('\n', out);
30373049

3038-
/* Account for what all of those arena bytes are being used for. */
3050+
/* Account for what all of those arena bytes are being used for. */
30393051
total = printone(out, "# bytes in allocated blocks", allocated_bytes);
30403052
total += printone(out, "# bytes in available blocks", available_bytes);
30413053

Python/ceval.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5171,6 +5171,9 @@ _PyEval_Vector(PyThreadState *tstate, PyFrameConstructor *con,
51715171
_PyObject_GC_TRACK(f);
51725172
if (_PyFrame_MakeCopyOfLocals(f)) {
51735173
Py_XDECREF(retval);
5174+
if (!is_coro) {
5175+
_PyThreadState_PopLocals(tstate, localsarray);
5176+
}
51745177
return NULL;
51755178
}
51765179
}

Python/pystate.c

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,9 @@ PyInterpreterState_GetDict(PyInterpreterState *interp)
607607
return interp->dict;
608608
}
609609

610+
#define DATA_STACK_SIZE (62*1024)
611+
#define DATA_STACK_HEADROOM (2*1024)
612+
610613
static PyThreadState *
611614
new_threadstate(PyInterpreterState *interp, int init)
612615
{
@@ -658,6 +661,15 @@ new_threadstate(PyInterpreterState *interp, int init)
658661

659662
tstate->context = NULL;
660663
tstate->context_ver = 1;
664+
size_t total_size = (DATA_STACK_SIZE+DATA_STACK_HEADROOM);
665+
tstate->datastack_base = _PyObject_VirtualAlloc(sizeof(PyObject *)*total_size);
666+
if (tstate->datastack_base == NULL) {
667+
PyMem_RawFree(tstate);
668+
return NULL;
669+
}
670+
tstate->datastack_top = tstate->datastack_base;
671+
tstate->datastack_hard_limit = tstate->datastack_base + total_size;
672+
tstate->datastack_soft_limit = tstate->datastack_base + DATA_STACK_SIZE;
661673

662674
if (init) {
663675
_PyThreadState_Init(tstate);
@@ -906,7 +918,6 @@ tstate_delete_common(PyThreadState *tstate,
906918
}
907919
}
908920

909-
910921
static void
911922
_PyThreadState_Delete(PyThreadState *tstate, int check_current)
912923
{
@@ -917,6 +928,7 @@ _PyThreadState_Delete(PyThreadState *tstate, int check_current)
917928
}
918929
}
919930
tstate_delete_common(tstate, gilstate);
931+
_PyObject_VirtualFree(tstate->datastack_base, sizeof(PyObject *)*DATA_STACK_SIZE);
920932
PyMem_RawFree(tstate);
921933
}
922934

@@ -1969,17 +1981,27 @@ _Py_GetConfig(void)
19691981
return _PyInterpreterState_GetConfig(tstate->interp);
19701982
}
19711983

1972-
/* Dumbest possible (and very inefficient) implementation */
1973-
19741984
PyObject **
19751985
_PyThreadState_PushLocals(PyThreadState *tstate, int size)
19761986
{
1977-
(void)tstate;
1978-
PyObject **res = PyMem_Malloc(sizeof(PyObject **)*size);
1979-
if (res == NULL) {
1980-
PyErr_NoMemory();
1987+
PyObject **res = tstate->datastack_top;
1988+
PyObject **top = res + size;
1989+
if (top >= tstate->datastack_soft_limit) {
1990+
if (top >= tstate->datastack_hard_limit) {
1991+
if (tstate->recursion_headroom) {
1992+
Py_FatalError("Cannot recover from data-stack overflow.");
1993+
}
1994+
else {
1995+
Py_FatalError("Rapid data-stack overflow.");
1996+
}
1997+
}
1998+
tstate->recursion_headroom++;
1999+
_PyErr_Format(tstate, PyExc_RecursionError,
2000+
"data stack overflow");
2001+
tstate->recursion_headroom--;
19812002
return NULL;
19822003
}
2004+
tstate->datastack_top = top;
19832005
for (Py_ssize_t i=0; i < size; i++) {
19842006
res[i] = NULL;
19852007
}
@@ -1989,8 +2011,8 @@ _PyThreadState_PushLocals(PyThreadState *tstate, int size)
19892011
void
19902012
_PyThreadState_PopLocals(PyThreadState *tstate, PyObject **locals)
19912013
{
1992-
(void)tstate;
1993-
PyMem_Free(locals);
2014+
assert(tstate->datastack_top >= locals);
2015+
tstate->datastack_top = locals;
19942016
}
19952017

19962018

0 commit comments

Comments
 (0)