Skip to content

Commit 6ddcaf4

Browse files
author
Anselm Kruis
committed
Stackless issue python#94: call __init__(self, ...) stackless
This commit makes calls to an initialiser method (__init__(self)) stackless, if soft switching is enabled. https://bitbucket.org/stackless-dev/stackless/issues/94 (grafted from dc2e43c1dbd20f279704c287eb0e2b69e0d14c7f and 43fa00988749)
1 parent f2607c9 commit 6ddcaf4

7 files changed

Lines changed: 273 additions & 13 deletions

File tree

Objects/typeobject.c

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -715,9 +715,13 @@ type_repr(PyTypeObject *type)
715715
return rtn;
716716
}
717717

718+
static int
719+
slot_tp_init(PyObject *self, PyObject *args, PyObject *kwds);
720+
718721
static PyObject *
719722
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
720723
{
724+
STACKLESS_GETARG();
721725
PyObject *obj;
722726

723727
if (type->tp_new == NULL) {
@@ -741,10 +745,29 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
741745
if (!PyType_IsSubtype(Py_TYPE(obj), type))
742746
return obj;
743747
type = Py_TYPE(obj);
744-
if (type->tp_init != NULL &&
745-
type->tp_init(obj, args, kwds) < 0) {
746-
Py_DECREF(obj);
747-
obj = NULL;
748+
if (type->tp_init != NULL) {
749+
initproc tp_init = type->tp_init;
750+
int ret;
751+
#ifdef STACKLESS
752+
int is_stackless;
753+
if (tp_init == slot_tp_init)
754+
STACKLESS_PROMOTE_ALL();
755+
is_stackless = slp_try_stackless;
756+
#endif
757+
ret = tp_init(obj, args, kwds);
758+
#ifdef STACKLESS
759+
STACKLESS_ASSERT();
760+
if (is_stackless && ret == STACKLESS_UNWINDING_MAGIC) {
761+
/* It is a stackless call and it is unwinding.
762+
* the real result is in Py_UnwindToken */
763+
Py_DECREF(obj);
764+
return (PyObject *)Py_UnwindToken;
765+
}
766+
#endif
767+
if (ret < 0) {
768+
Py_DECREF(obj);
769+
obj = NULL;
770+
}
748771
}
749772
}
750773
return obj;
@@ -3008,6 +3031,7 @@ PyTypeObject PyType_Type = {
30083031
(inquiry)type_is_gc, /* tp_is_gc */
30093032
};
30103033

3034+
STACKLESS_DECLARE_METHOD(&PyType_Type, tp_call)
30113035

30123036
/* The base type of all types (eventually)... except itself. */
30133037

@@ -5696,18 +5720,67 @@ slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value)
56965720
return 0;
56975721
}
56985722

5723+
#ifdef STACKLESS
5724+
PyObject *
5725+
slp_tp_init_callback(PyFrameObject *f, int exc, PyObject *retval)
5726+
{
5727+
PyThreadState *ts = PyThreadState_GET();
5728+
PyCFrameObject *cf = (PyCFrameObject *) f;
5729+
5730+
f = cf->f_back;
5731+
if (retval != NULL) {
5732+
if (retval != Py_None) {
5733+
PyErr_Format(PyExc_TypeError,
5734+
"__init__() should return None, not '%.200s'",
5735+
Py_TYPE(retval)->tp_name);
5736+
Py_DECREF(retval);
5737+
retval = NULL;
5738+
} else {
5739+
Py_DECREF(retval);
5740+
retval = cf->ob1;
5741+
cf->ob1 = NULL;
5742+
}
5743+
}
5744+
ts->frame = f;
5745+
Py_DECREF(cf);
5746+
return STACKLESS_PACK(retval);
5747+
}
5748+
#endif
5749+
56995750
static int
57005751
slot_tp_init(PyObject *self, PyObject *args, PyObject *kwds)
57015752
{
5702-
STACKLESS_GETARG(); /* not yet supported */
5753+
STACKLESS_GETARG();
57035754
_Py_IDENTIFIER(__init__);
57045755
PyObject *meth = lookup_method(self, &PyId___init__);
57055756
PyObject *res;
57065757

57075758
if (meth == NULL)
57085759
return -1;
5760+
#ifdef STACKLESS
5761+
if (stackless) {
5762+
PyCFrameObject *f = slp_cframe_new(slp_tp_init_callback, 1);
5763+
if (f == NULL)
5764+
return -1;
5765+
Py_INCREF(self);
5766+
f->ob1 = self;
5767+
PyThreadState_GET()->frame = (PyFrameObject *) f;
5768+
}
5769+
#endif
5770+
STACKLESS_PROMOTE_ALL();
57095771
res = PyObject_Call(meth, args, kwds);
5772+
STACKLESS_ASSERT();
57105773
Py_DECREF(meth);
5774+
#ifdef STACKLESS
5775+
if (stackless && !STACKLESS_UNWINDING(res)) {
5776+
/* required, because added a C-frame */
5777+
STACKLESS_PACK(res);
5778+
return STACKLESS_UNWINDING_MAGIC;
5779+
}
5780+
if (STACKLESS_UNWINDING(res)) {
5781+
return STACKLESS_UNWINDING_MAGIC;
5782+
}
5783+
#endif
57115784
if (res == NULL)
57125785
return -1;
57135786
if (res != Py_None) {

Stackless/changelog.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ What's New in Stackless 3.X.X?
99

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

12+
- https://bitbucket.org/stackless-dev/stackless/issues/94
13+
Calls to __init__(self, ...) are now stackless, if
14+
soft-switching is enabled.
15+
Note: pickling and unpickling an object while its __init__() method runs,
16+
may cause unexpected interactions between the object reconstruction and the
17+
remaining part of __init__().
18+
1219
- https://bitbucket.org/stackless-dev/stackless/issues/98
1320
Prevent an unlikely NULL-pointer access in safe_pickle.c pickle_callback().
1421
The bug is very old and might affect multiprocessing.

Stackless/core/stackless_impl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ PyAPI_FUNC(PyObject *) PyEval_EvalFrame_with_cleanup(struct _frame *f, int thro
114114
/* other eval_frame functions from module/scheduling.c */
115115
PyAPI_FUNC(PyObject *) slp_restore_exception(PyFrameObject *f, int exc, PyObject *retval);
116116
PyAPI_FUNC(PyObject *) slp_restore_tracing(PyFrameObject *f, int exc, PyObject *retval);
117+
/* other eval_frame functions from Objects/typeobject.c */
118+
PyObject * slp_tp_init_callback(PyFrameObject *f, int exc, PyObject *retval);
117119

118120
/* rebirth of software stack avoidance */
119121

@@ -173,6 +175,9 @@ PyAPI_FUNC(PyTaskletObject *) slp_get_watchdog(PyThreadState *ts, int interrupt)
173175
#define STACKLESS_UNWINDING(obj) \
174176
((PyObject *) (obj) == (PyObject *) Py_UnwindToken)
175177

178+
/* an arbitrary positive number */
179+
#define STACKLESS_UNWINDING_MAGIC 0x7fedcba9
180+
176181
/* macros for setting/resetting the stackless flag */
177182

178183
#define STACKLESS_GETARG() int stackless = (stackless = slp_try_stackless, \

Stackless/core/stackless_methods.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* this file was generated from the Python C sources using the script
2+
* this file was generated from the Python(r) C sources using the script
33
* Stackless/core/extract_slp_methods.py .
44
* please don't edit this output, but work on the script.
55
*/
@@ -15,18 +15,20 @@ typedef struct {
1515

1616
static _stackless_method _stackless_methtable[] = {
1717
/* from classobject.c */
18-
{&PyMethod_Type, MFLAG_OFS(tp_call)},
18+
{&PyMethod_Type, MFLAG_OFS(tp_call)},
1919
/* from descrobject.c */
2020
{&PyMethodDescr_Type, MFLAG_OFS(tp_call)},
2121
{&PyClassMethodDescr_Type, MFLAG_OFS(tp_call)},
22-
{&_PyMethodWrapper_Type, MFLAG_OFS(tp_call)},
22+
{&_PyMethodWrapper_Type, MFLAG_OFS(tp_call)},
2323
/* from funcobject.c */
24-
{&PyFunction_Type, MFLAG_OFS(tp_call)},
24+
{&PyFunction_Type, MFLAG_OFS(tp_call)},
2525
/* from genobject.c */
26-
{&PyGen_Type, MFLAG_OFS(tp_iternext)},
26+
{&PyGen_Type, MFLAG_OFS(tp_iternext)},
2727
/* from methodobject.c */
28-
{&PyCFunction_Type, MFLAG_OFS(tp_call)},
28+
{&PyCFunction_Type, MFLAG_OFS(tp_call)},
29+
/* from typeobject.c */
30+
{&PyType_Type, MFLAG_OFS(tp_call)},
2931
/* from channelobject.c */
30-
{&PyChannel_Type, MFLAG_OFS(tp_iternext)},
32+
{&PyChannel_Type, MFLAG_OFS(tp_iternext)},
3133
{0, 0} /* sentinel */
3234
};

Stackless/pickling/prickelpit.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,7 @@ DEF_INVALID_EXEC(eval_frame_with_cleanup)
744744
DEF_INVALID_EXEC(channel_seq_callback)
745745
DEF_INVALID_EXEC(slp_restore_exception)
746746
DEF_INVALID_EXEC(slp_restore_tracing)
747+
DEF_INVALID_EXEC(slp_tp_init_callback)
747748

748749
static PyTypeObject wrap_PyFrame_Type;
749750

@@ -1126,6 +1127,8 @@ static int init_frametype(void)
11261127
slp_restore_exception, REF_INVALID_EXEC(slp_restore_exception))
11271128
|| slp_register_execute(&PyCFrame_Type, "slp_restore_tracing",
11281129
slp_restore_tracing, REF_INVALID_EXEC(slp_restore_tracing))
1130+
|| slp_register_execute(&PyCFrame_Type, "slp_tp_init_callback",
1131+
slp_tp_init_callback, REF_INVALID_EXEC(slp_tp_init_callback))
11291132
|| init_type(&wrap_PyFrame_Type, initchain);
11301133
}
11311134
#undef initchain

Stackless/unittests/test_pickle.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,20 @@ def ctxpickling(testCase, n, when, expect_type, suppress_exc):
224224
return "OK"
225225

226226

227+
class InitTestClass:
228+
def __init__(self, testcase):
229+
testcase.started = True
230+
schedule()
231+
232+
233+
def inittest(testcase, cls):
234+
# test pickling of stackless __init__
235+
testcase.started = False
236+
obj = cls(testcase)
237+
testcase.assertTrue(testcase.started)
238+
return "OK"
239+
240+
227241
def is_soft():
228242
softswitch = stackless.enable_softswitch(0)
229243
stackless.enable_softswitch(softswitch)
@@ -453,6 +467,9 @@ def testCtx_exit_3(self):
453467
def testCtx_exit_4(self):
454468
self.run_pickled(ctxpickling, self, 1, 'exit', RuntimeError, True)
455469

470+
def test__init__(self):
471+
self.run_pickled(inittest, self, InitTestClass)
472+
456473
def testFakeModules(self):
457474
types.ModuleType('fakemodule!')
458475

0 commit comments

Comments
 (0)