8000 merge 3.4-slp (Stackless #94) · akruis/cpython@c86af92 · GitHub
[go: up one dir, main page]

Skip to content 8000

Commit c86af92

Browse files
author
Anselm Kruis
committed
merge 3.4-slp (Stackless python#94)
2 parents 44c7230 + 8d26499 commit c86af92

File tree

7 files changed

+261
-3
lines changed

7 files changed

+261
-3
lines changed

Objects/typeobject.c

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -887,9 +887,13 @@ type_repr(PyTypeObject *type)
887887
return rtn;
888888
}
889889

890+
static int
891+
slot_tp_init(PyObject *self, PyObject *args, PyObject *kwds);
892+
890893
static PyObject *
891894
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
892895
{
896+
STACKLESS_GETARG();
893897
PyObject *obj;
894898

895899
if (type->tp_new == NULL) {
@@ -921,7 +925,24 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
921925
return obj;
922926
type = Py_TYPE(obj);
923927
if (type->tp_init != NULL) {
924-
int res = type->tp_init(obj, args, kwds);
928+
initproc tp_init = type->tp_init;
929+
int res;
930+
#ifdef STACKLESS
931+
int is_stackless;
932+
if (tp_init == slot_tp_init)
933+
STACKLESS_PROMOTE_ALL();
934+
is_stackless = slp_try_stackless;
935+
#endif
936+
res = tp_init(obj, args, kwds);
937+
#ifdef STACKLESS
938+
STACKLESS_ASSERT();
939+
if (is_stackless && res == STACKLESS_UNWINDING_MAGIC) {
940+
/* It is a stackless call and it is unwinding.
941+
* the real result is in Py_UnwindToken */
942+
Py_DECREF(obj);
943+
return (PyObject *)Py_UnwindToken;
944+
}
945+
#endif
925946
if (res < 0) {
926947
Py_DECREF(obj);
927948
obj = NULL;
@@ -3342,6 +3363,7 @@ PyTypeObject PyType_Type = {
33423363
(inquiry)type_is_gc, /* tp_is_gc */
33433364
};
33443365

3366+
STACKLESS_DECLARE_METHOD(&PyType_Type, tp_call)
33453367

33463368
/* The base type of all types (eventually)... except itself. */
33473369

@@ -6311,18 +6333,67 @@ slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value)
63116333
return 0;
63126334
}
63136335

6336+
#ifdef STACKLESS
6337+
PyObject *
6338+
slp_tp_init_callback(PyFrameObject *f, int exc, PyObject *retval)
6339+
{
6340+
PyThreadState *ts = PyThreadState_GET();
6341+
PyCFrameObject *cf = (PyCFrameObject *) f;
6342+
6343+
f = cf->f_back;
6344+
if (retval != NULL) {
6345+
if (retval != Py_None) {
6346+
PyErr_Format(PyExc_TypeError,
6347+
"__init__() should return None, not '%.200s'",
6348+
Py_TYPE(retval)->tp_name);
6349+
Py_DECREF(retval);
6350+
retval = NULL;
6351+
} else {
6352+
Py_DECREF(retval);
6353+
retval = cf->ob1;
6354+
cf->ob1 = NULL;
6355+
}
6356+
}
6357+
ts->frame = f;
6358+
Py_DECREF(cf);
6359+
return STACKLESS_PACK(retval);
6360+
}
6361+
#endif
6362+
63146363
static int
63156364
slot_tp_init(PyObject *self, PyObject *args, PyObject *kwds)
63166365
{
6317-
STACKLESS_GETARG(); /* not yet supported */
6366+
STACKLESS_GETARG();
63186367
_Py_IDENTIFIER(__init__);
63196368
PyObject *meth = lookup_method(self, &PyId___init__);
63206369
PyObject *res;
63216370

63226371
if (meth == NULL)
63236372
return -1;
6373+
#ifdef STACKLESS
6374+
if (stackless) {
6375+
PyCFrameObject *f = slp_cframe_new(slp_tp_init_callback, 1);
6376+
if (f == NULL)
6377+
return -1;
6378+
Py_INCREF(self);
6379+
f->ob1 = self;
6380+
PyThreadState_GET()->frame = (PyFrameObject *) f;
6381+
}
6382+
#endif
6383+
STACKLESS_PROMOTE_ALL();
63246384
res = PyObject_Call(meth, args, kwds);
6385+
STACKLESS_ASSERT();
63256386
Py_DECREF(meth);
6387+
#ifdef STACKLESS
6388+
if (stackless && !STACKLESS_UNWINDING(res)) {
6389+
/* required, because added a C-frame */
6390+
STACKLESS_PACK(res);
6391+
return STACKLESS_UNWINDING_MAGIC;
6392+
}
6393+
if (STACKLESS_UNWINDING(res)) {
6394+
return STACKLESS_UNWINDING_MAGIC;
6395+
}
6396+
#endif
63266397
if (res == NULL)
63276398
return -1;
63286399
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
#define STACKLESS_RETVAL(obj) \
177182
(STACKLESS_UNWINDING(obj) ? Py_UnwindToken->tempval : (obj))
178183

Stackless/core/stackless_methods.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ static _stackless_method _stackless_methtable[] = {
2626
{&PyGen_Type, MFLAG_OFS(tp_iternext)},
2727
/* from methodobject.c */
2828
{&PyCFunction_Type, MFLAG_OFS(tp_call)},
29+
/* from typeobject.c */
30+
{&PyType_Type, MFLAG_OFS(tp_call)},
2931
/* from channelobject.c */
3032
{&PyChannel_Type, MFLAG_OFS(tp_iternext)},
3133
{0, 0} /* sentinel */

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
@@ -223,6 +223,20 @@ def ctxpickling(testCase, n, when, expect_type, suppress_exc):
223223
return "OK"
224224

225225

226+
class InitTestClass:
227+
def __init__(self, testcase):
228+
testcase.started = True
229+
schedule()
230+
231+
232+
def inittest(testcase, cls):
233+
# test pickling of stackless __init__
234+
testcase.started = False
235+
obj = cls(testcase)
236+
testcase.assertTrue(testcase.started)
237+
return "OK"
238+
239+
226240
def is_soft():
227241
softswitch = stackless.enable_softswitch(0)
228242
stackless.enable_softswitch(softswitch)
@@ -448,6 +462,9 @@ def testCtx_exit_3(self):
448462< F438 div class="diff-text-inner"> def testCtx_exit_4(self):
449463
self.run_pickled(ctxpickling, self, 1, 'exit', RuntimeError, True)
450464

465+
def test__init__(self):
466+
self.run_pickled(inittest, self, InitTestClass)
467+
451468
def testFakeModules(self):
452469
types.ModuleType('fakemodule!')
453470

Stackless/unittests/test_tpflags_have_stackless_call.py

Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
import unittest
2525
import stackless
26+
import gc
27+
import sys
2628
from support import StacklessTestCase
2729

2830

@@ -235,8 +237,159 @@ def testCallIndirect(self):
235237
stackless.tasklet(self.callIndirect)()
236238
stackless.run()
237239

240+
241+
class TestInitIsStackless(StacklessTestCase):
242+
"""Test, if __init__ supports the stackless protocoll"""
243+
def nestingLevelTest(self, cls):
244+
# check the nesting level and the ref counts
245+
def func(obj=None):
246+
if obj is None:
247+
self.assertGreater(stackless.current.nesting_level, 0)
248+
return None
249+
self.assertIs(obj.__class__, cls)
250+
obj.nesting_level = stackless.current.nesting_level
251+
return None
252+
253+
with stackless.atomic():
254+
# None-refcount it needs protection, it the test suite is multithreaded
255+
gc.collect()
256+
rc_none = sys.getrefcount(None)
257+
rc_cls = sys.getrefcount(cls)
258+
c = cls(func)
259+
self.assertEqual(sys.getrefcount(None), rc_none)
260+
self.assertEqual(sys.getrefcount(c) - sys.getrefcount(object()), 1)
261+
self.assertIs(c.__class__, cls)
262+
current_nesting_level = stackless.current.nesting_level
263+
if hasattr(c, "nesting_level"):
264+
if stackless.enable_softswitch(None):
265+
self.assertEqual(c.nesting_level, current_nesting_level)
266+
else:
267+
self.assertGreater(c.nesting_level, current_nesting_level)
268+
c = None
269+
gc.collect()
270+
self.assertEqual(sys.getrefcount(cls), rc_cls)
271+
272+
def C__init__(self, func):
273+
return func(self)
274+
275+
class C:
276+
def __init__(self, func):
277+
return func(self)
278+
279+
class CSyn:
280+
pass
281+
CSyn.__init__ = C__init__
282+
283+
class CNew(object):
284+
def __init__(self, func):
285+
return func(self)
286+
287+
class CNewSyn(object):
288+
pass
289+
CNewSyn.__init__ = C__init__
290+
291+
class CNonStackless:
292+
__init__ = stackless._test_nostacklesscall
293+
294+
class CNewNonStackless(object):
295+
__init__ = stackless._test_nostacklesscall
296+
297+
def testNestingLevelNew(self):
298+
self.nestingLevelTest(self.CNew)
299+
300+
def testNestingLevelNewSynthetic(self):
301+
self.nestingLevelTest(self.CNewSyn)
302+
303+
def testNestingLevel(self):
304+
self.nestingLevelTest(self.C)
305+
306+
def testNestingLevelSynthetic(self):
307+
self.nestingLevelTest(self.CSyn)
308+
309+
def testNestingLevelNonStackless(self):
310+
self.nestingLevelTest(self.CNonStackless)
311+
312+
def testNestingLevelNewNonStackless(self):
313+
self.nestingLevelTest(self.CNewNonStackless)
314+
315+
def returnTypeTest(self, cls):
316+
# check the nesting level and the ref counts
317+
def func(obj):
318+
return "not None"
319+
320+
def helper():
321+
# we need a close control over references to None here
322+
try:
323+
cls(func)
324+
except TypeError:
325+
emsg = sys.exc_info()[1].args[0]
326+
return emsg
327+
self.fail("does not raise the expected exception")
328+
329+
gc.collect()
330+
with stackless.atomic():
331+
rc_none = sys.getrefcount(None)
332+
rc_cls = sys.getrefcount(cls)
333+
# we need a close control over references to None here
334+
emsg = helper()
335+
rc_none2 = sys.getrefcount(None)
336+
rc_cls2 = sys.getrefcount(cls)
337+
self.assertEqual(rc_cls, rc_cls2)
338+
self.assertEqual(rc_none, rc_none2)
339+
self.assertIn("__init__() should return None", emsg)
340+
341+
def testReturnType(self):
342+
self.returnTypeTest(self.C)
343+
344+
def testReturnTypeSynthetic(self):
345+
self.returnTypeTest(self.CSyn)
346+
347+
def testReturnTypeNew(self):
348+
self.returnTypeTest(self.CNew)
349+
350+
def testReturnTypeNewSynthetic(self):
351+
self.returnTypeTest(self.CNewSyn)
352+
353+
def exceptionTest(self, cls):
354+
# check the nesting level and the ref counts
355+
def func(obj):
356+
1 / 0
357+
358+
def helper():
359+
# we need a close control over references to None here
360+
try:
361+
cls(func)
362+
except ZeroDivisionError:
363+
emsg = sys.exc_info()[1].args[0]
364+
return emsg
365+
self.fail("does not raise the expected exception")
366+
367+
emsg = ''
368+
gc.collect()
369+
with stackless.atomic():
370+
rc_none = sys.getrefcount(None)
371+
rc_cls = sys.getrefcount(cls)
372+
# we need a close control over references to None here
373+
emsg = helper()
374+
rc_none2 = sys.getrefcount(None)
375+
rc_cls2 = sys.getrefcount(cls)
376+
self.assertEqual(rc_cls, rc_cls2)
377+
self.assertEqual(rc_none, rc_none2)
378+
self.assertIn("division by zero", emsg)
379+
380+
def testException(self):
381+
self.exceptionTest(self.C)
382+
383+
def testExceptionSynthetic(self):
384+
self.exceptionTest(self.CSyn)
385+
386+
def testExceptionNew(self):
387+
self.exceptionTest(self.CNew)
388+
389+
def testExceptionNewSynthetic(self):
390+
self.exceptionTest(self.CNewSyn)
391+
238392
if __name__ == "__main__":
239-
import sys
240393
if not sys.argv[1:]:
241394
sys.argv.append('-v')
242395
unittest.main()

0 commit comments

Comments
 (0)
0