Skip to content

Commit 441c7d8

Browse files
Anselm KruisAnselm Kruis
authored andcommitted
Stackless issue python#186: Make Stackless compatible with subinterpreters.
Previously creating a subinterpreter in one thread and using it in another thread caused a crash. Now Stackless resets the thread state, once all C-stacks have been executed.
1 parent 5d63c61 commit 441c7d8

File tree

7 files changed

+62
-21
lines changed

7 files changed

+62
-21
lines changed

Python/pystate.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,11 @@ tstate_delete_common(PyThreadState *tstate)
598598
if (tstate->next)
599599
tstate->next->prev = tstate->prev;
600600
HEAD_UNLOCK();
601+
#ifdef STACKLESS
602+
if (interp->st.initial_tstate == tstate) {
603+
interp->st.initial_tstate = NULL;
604+
}
605+
#endif
601606
if (tstate->on_delete != NULL) {
602607
tstate->on_delete(tstate->on_delete_data);
603608
}

Stackless/changelog.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ What's New in Stackless 3.X.X?
99

1010
*Release date: 20XX-XX-XX*
1111

12+
- https://github.com/stackless-dev/stackless/issues/186
13+
Make Stackless compatible with subinterpreters. Previously creating a
14+
subinterpreter in one thread and using it in another thread caused a crash.
15+
1216
- https://github.com/stackless-dev/stackless/issues/182
13-
Fix "make teststackless" for out of tree builds
17+
Fix "make teststackless" for out of tree builds.
1418

1519
- https://github.com/stackless-dev/stackless/issues/180
1620
Deprecate stackless.pickle_with_tracing_state. It has been replaced by

Stackless/core/stackless_tstate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ typedef struct {
3838
uint8_t pickleflags; /* flags for pickling / unpickling */
3939
} PyStacklessInterpreterState;
4040

41+
#define SLP_INITIAL_TSTATE(tstate) \
42+
(assert(tstate), \
43+
assert((tstate)->interp->st.initial_tstate), \
44+
(tstate)->interp->st.initial_tstate)
45+
4146
#define SPL_INTERPRETERSTATE_NEW(interp) \
4247
(interp)->st.cstack_chain = NULL; \
4348
(interp)->st.reduce_frame_func = NULL; \

Stackless/core/stacklesseval.c

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -319,13 +319,6 @@ slp_eval_frame(PyFrameObject *f)
319319
PyObject *retval;
320320

321321
if (fprev == NULL && ts->st.main == NULL) {
322-
int returning;
323-
if (ts->interp->st.mem_bomb == NULL) {
324-
/* allocate the permanent bomb to use for mem errors */
325-
if ((ts->interp->st.mem_bomb = slp_new_bomb()) == NULL)
326-
return NULL;
327-
}
328-
329322
/* this is the initial frame, so mark the stack base */
330323

331324
/*
@@ -337,12 +330,46 @@ slp_eval_frame(PyFrameObject *f)
337330
* with a __del__ method is destroyed. This __del__
338331
* will run as a toplevel frame, with f_back == NULL!
339332
*/
333+
int returning;
340334

335+
/* complete the interpreter state */
336+
if (ts->interp->st.initial_tstate == NULL) {
337+
ts->interp->st.initial_tstate = ts;
338+
}
339+
if (ts->interp->st.mem_bomb == NULL) {
340+
/* allocate the permanent bomb to use for mem errors */
341+
if ((ts->interp->st.mem_bomb = slp_new_bomb()) == NULL)
342+
return NULL;
343+
}
344+
345+
/* mark the stack base */
341346
stackref = STACK_REFPLUS + (intptr_t *) &f;
342347
if (ts->st.cstack_base == NULL)
343348
ts->st.cstack_base = stackref - CSTACK_GOODGAP;
344-
if (stackref > ts->st.cstack_base)
345-
return climb_stack_and_eval_frame(f);
349+
if (stackref > ts->st.cstack_base) {
350+
PyCStackObject *cst;
351+
retval = climb_stack_and_eval_frame(f);
352+
cst = ts->st.initial_stub;
353+
/* might be NULL in OOM conditions */
354+
if (cst != NULL) {
355+
PyCStackObject *other;
356+
register int found = 0;
357+
assert(cst->startaddr == ts->st.cstack_base);
358+
for (other = cst->next; other != cst; other = other->next) {
359+
if (Py_SIZE(other) != 0 && other->task != NULL && other->tstate == ts) {
360+
assert(other->startaddr == ts->st.cstack_base);
361+
found = 1;
362+
break;
363+
}
364+
}
365+
if (!found) {
366+
Py_CLEAR(ts->st.initial_stub);
367+
ts->st.cstack_base = NULL;
368+
ts->st.cstack_root = NULL;
369+
}
370+
}
371+
return retval;
372+
}
346373

347374
assert(SLP_CURRENT_FRAME(ts) == NULL); /* else we would change the current frame */
348375
SLP_STORE_NEXT_FRAME(ts, f);
@@ -358,9 +385,11 @@ slp_eval_frame(PyFrameObject *f)
358385
if (returning != 1) {
359386
assert(f == SLP_PEEK_NEXT_FRAME(ts));
360387
}
388+
assert(ts->interp->st.initial_tstate != NULL);
361389
assert(ts->interp->st.mem_bomb != NULL);
362390
return slp_run_tasklet();
363391
}
392+
assert(ts->interp->st.initial_tstate != NULL);
364393
assert(ts->interp->st.mem_bomb != NULL); /* see above */
365394
Py_INCREF(Py_None);
366395
Py_XINCREF(fprev);

Stackless/module/scheduling.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ schedule_task_block(PyObject **result, PyTaskletObject *prev, int stackless, int
842842

843843
cantblock:
844844
/* cannot block */
845-
if (revive_main || (ts == ts->interp->st.initial_tstate && wakeup->next == NULL)) {
845+
if (revive_main || (ts == SLP_INITIAL_TSTATE(ts) && wakeup->next == NULL)) {
846846
/* emulate old revive_main behavior:
847847
* passing a value only if it is an exception
848848
*/
@@ -1064,7 +1064,7 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject
10641064
prev->cstate = ts->st.initial_stub;
10651065
Py_INCREF(prev->cstate);
10661066
}
1067-
if (ts != ts->interp->st.initial_tstate) {
1067+
if (ts != SLP_INITIAL_TSTATE(ts)) {
10681068
/* ensure to get all tasklets into the other thread's chain */
10691069
if (slp_ensure_linkage(prev) || slp_ensure_linkage(next))
10701070
return -1;
@@ -1394,9 +1394,11 @@ slp_tasklet_end(PyObject *retval)
13941394
int handled = 0;
13951395
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
13961396
/* but if it is truly a SystemExit on the main thread, we want the exit! */
1397-
if (ts == ts->interp->st.initial_tstate && !PyErr_ExceptionMatches(PyExc_TaskletExit)) {
1398-
PyStackless_HandleSystemExit();
1399-
handled = 1; /* handler returned, it wants us to silence it */
1397+
if (ts == SLP_INITIAL_TSTATE(ts) && !PyErr_ExceptionMatches(PyExc_TaskletExit)) {
1398+
if (ts->interp == _PyRuntime.interpreters.main) {
1399+
PyStackless_HandleSystemExit();
1400+
handled = 1; /* handler returned, it wants us to silence it */
1401+
}
14001402
} else if (!ismain) {
14011403
/* deal with TaskletExit on a non-main tasklet */
14021404
handled = 1;

Stackless/module/stacklessmodule.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,10 +1753,6 @@ static int init_stackless_methods(void)
17531753
int
17541754
_PyStackless_InitTypes(void)
17551755
{
1756-
/* record the thread state for thread support */
1757-
PyThreadState * ts = PyThreadState_GET();
1758-
ts->interp->st.initial_tstate = ts;
1759-
17601756
if (0
17611757
|| slp_init_bombtype()
17621758
|| init_stackless_methods()

Stackless/module/taskletobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ tasklet_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
453453
t->cstate = ts->st.initial_stub;
454454
t->def_globals = PyEval_GetGlobals();
455455
Py_XINCREF(t->def_globals);
456-
if (ts != ts->interp->st.initial_tstate) {
456+
if (ts != SLP_INITIAL_TSTATE(ts)) {
457457
/* make sure to kill tasklets with their thread */
458458
if (slp_ensure_linkage(t)) {
459459
Py_DECREF(t);
@@ -1153,7 +1153,7 @@ bind_tasklet_to_frame(PyTaskletObject *task, PyFrameObject *frame)
11531153
task->cstate = ts->st.initial_stub;
11541154
Py_INCREF(task->cstate);
11551155
Py_DECREF(hold);
1156-
if (ts != ts->interp->st.initial_tstate)
1156+
if (ts != SLP_INITIAL_TSTATE(ts))
11571157
if (slp_ensure_linkage(task))
11581158
return -1;
11591159
}

0 commit comments

Comments
 (0)