Skip to content

Commit 35ba34f

Browse files
author
kristjan.jonsson
committed
Path gcmodule so that objects themselves can decide if they can be collected.
Make tasklet objects uncollectable if they belong to a different thread than the one doing the collection. Otherwise, they would get inserted into the target thread's queue, but their frame object would have been cleared and they were defunct. Killing tasklets works on the same thread because then task switching can be made immediate. This fixes a very hard to repro bug with test/taskspeed.py git-svn-id: http://svn.python.org/projects/stackless/trunk@82436 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 5b5f981 commit 35ba34f

File tree

6 files changed

+56
-29
lines changed

6 files changed

+56
-29
lines changed

Include/objimpl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,8 @@ PyAPI_FUNC(PyVarObject *) _PyObject_GC_NewVar(PyTypeObject *, Py_ssize_t);
309309
PyAPI_FUNC(void) PyObject_GC_Track(void *);
310310
PyAPI_FUNC(void) PyObject_GC_UnTrack(void *);
311311
PyAPI_FUNC(void) PyObject_GC_Del(void *);
312+
/* STACKLESS addition */
313+
PyAPI_FUNC(void) PyObject_GC_Collectable(PyObject *, visitproc, void*, int);
312314

313315
#define PyObject_GC_New(type, typeobj) \
314316
( (type *) _PyObject_GC_New(typeobj) )

Modules/gcmodule.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,34 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
456456
}
457457
}
458458

459+
/* STACKLESS addition to support collection of tasklets */
460+
461+
/* A traversal callback for has_finalisers. It is a dummy, used to identify
462+
* this phase of finalizer discovery by its function pointer, and thus
463+
* enable the action of PyObject_GC_Collectable() below.
464+
*/
465+
static int
466+
visit_has_finalizer(PyObject *op, void *data)
467+
{
468+
assert(op != NULL);
469+
return 0;
470+
}
471+
472+
/* An object can call this function from its traversal function
473+
* to indicate whether it wishes to be collected or not
474+
* this is useful for objects that have state that determines
475+
* whether non-trivial actions need to be undertaken when
476+
* it is deleted.
477+
*/
478+
PyAPI_FUNC(void)
479+
PyObject_GC_Collectable(PyObject *op, visitproc proc, void *arg,
480+
int can_collect)
481+
{
482+
/* only do this during the move_finalizers phase */
483+
if (proc == &visit_has_finalizer)
484+
*(int*)arg = can_collect;
485+
}
486+
459487
/* Return true if object has a finalization method.
460488
* CAUTION: An instance of an old-style class has to be checked for a
461489
*__del__ method, and earlier versions of this used to call PyObject_HasAttr,
@@ -466,6 +494,17 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
466494
static int
467495
has_finalizer(PyObject *op)
468496
{
497+
/* first, dynamic decision per object */
498+
traverseproc traverse;
499+
int collectable;
500+
traverse = Py_TYPE(op)->tp_traverse;
501+
collectable = -1;
502+
(void) traverse(op,
503+
(visitproc)visit_has_finalizer,
504+
(void *)&collectable);
505+
if (collectable >= 0)
506+
return collectable == 0;
507+
469508
if (PyInstance_Check(op)) {
470509
assert(delstr != NULL);
471510
return _PyInstance_Lookup(op, delstr) != NULL;

Stackless/core/cframeobject.c

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,10 @@ cframe_dealloc(PyCFrameObject *cf)
5454
static int
5555
cframe_traverse(PyCFrameObject *cf, visitproc visit, void *arg)
5656
{
57-
int err;
58-
59-
#define VISIT(o) if (o) {if ((err = visit((PyObject *)(o), arg))) return err;}
60-
61-
VISIT(cf->f_back);
62-
VISIT(cf->ob1);
63-
VISIT(cf->ob2);
64-
VISIT(cf->ob3);
65-
#undef VISIT
57+
Py_VISIT(cf->f_back);
58+
Py_VISIT(cf->ob1);
59+
Py_VISIT(cf->ob2);
60+
Py_VISIT(cf->ob3);
6661
return 0;
6762
}
6863

Stackless/module/channelobject.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,10 @@ channel_dealloc(PyObject *ob)
4949
static int
5050
channel_traverse(PyChannelObject *ch, visitproc visit, void *arg)
5151
{
52-
int err;
5352
PyTaskletObject *p;
54-
55-
#define VISIT(o) if (o) {if ((err = visit((PyObject *)(o), arg))) return err;}
5653
for (p = ch->head; p != (PyTaskletObject *) ch; p = p->next) {
57-
VISIT(p);
54+
Py_VISIT(p);
5855
}
59-
#undef VISIT
6056
return 0;
6157
}
6258

Stackless/module/scheduling.c

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,9 @@ bomb_dealloc(PyBombObject *bomb)
3636
static int
3737
bomb_traverse(PyBombObject *bomb, visitproc visit, void *arg)
3838
{
39-
int err;
40-
41-
#define VISIT(o) if (o) {if ((err = visit((PyObject *)(o), arg))) return err;}
42-
43-
VISIT(bomb->curexc_type);
44-
VISIT(bomb->curexc_value);
45-
VISIT(bomb->curexc_traceback);
46-
47-
#undef VISIT
39+
Py_VISIT(bomb->curexc_type);
40+
Py_VISIT(bomb->curexc_value);
41+
Py_VISIT(bomb->curexc_traceback);
4842
return 0;
4943
}
5044

Stackless/module/taskletobject.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,18 @@ slp_current_remove(void)
4747
static int
4848
tasklet_traverse(PyTaskletObject *t, visitproc visit, void *arg)
4949
{
50-
int err;
5150
PyFrameObject *f;
51+
PyThreadState *ts = PyThreadState_GET();
52+
if (ts != t->cstate->tstate)
53+
/* can't collect from this thread! */
54+
PyObject_GC_Collectable((PyObject *)t, visit, arg, 0);
5255

53-
#define VISIT(o) if (o) {if ((err = visit((PyObject *)(o), arg))) return err;}
5456
/* we own the "execute reference" of all the frames */
5557
for (f = t->f.frame; f != NULL; f = f->f_back) {
56-
VISIT(f);
58+
Py_VISIT(f);
5759
}
58-
VISIT(t->tempval);
59-
VISIT(t->cstate);
60-
#undef VISIT
60+
Py_VISIT(t->tempval);
61+
Py_VISIT(t->cstate);
6162
return 0;
6263
}
6364

0 commit comments

Comments
 (0)