Skip to content

Commit 70f5f79

Browse files
author
Anselm Kruis
committed
Issue python#106: tasklet.setup() now raises RuntimeError, if the tasklet is alive
If you call tasklet.setup(*args, **kwargs) and the tasklet is already alive, Stackless now raises RuntimeError. Previously the behaviour was undefined. Additionally this change updates the documentation of tasklet.setup() and adds a test case. https://bitbucket.org/stackless-dev/stackless/issues/106 (grafted from f25b5c2a3a90475f649c75333864e28dc8e50800)
1 parent baa885e commit 70f5f79

File tree

4 files changed

+54
-7
lines changed

4 files changed

+54
-7
lines changed

Doc/library/stackless/tasklets.rst

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ The ``tasklet`` class
7878
>>> t = stackless.tasklet()
7979
>>> t.bind(func)
8080
>>> t.setup(1, 2, 3, name="test")
81-
81+
82+
and
83+
8284
>>> t = stackless.tasklet()
8385
>>> t.bind(func, (1, 2, 3), {"name":"test"})
8486
>>> t.insert()
@@ -147,12 +149,17 @@ The ``tasklet`` class
147149
provided with arguments to pass to it, they are implicitly
148150
scheduled and will be run in turn when the scheduler is next run.
149151

150-
The above code is equivalent to::
151-
152-
>>> t = stackless.tasklet()
153-
>>> t.bind(func, (1, 2), {"name":"test"})
154-
>>> t.insert()
155-
152+
The method :meth:`setup` is equivalent to::
153+
154+
>>> def setup(self, *args, **kwargs):
155+
>>> assert isinstance(self, stackless.tasklet)
156+
>>> with stackless.atomic():
157+
>>> if self.alive:
158+
>>> raise(RuntimeError("tasklet is alive")
159+
>>> self.bind(None, args, kwargs)
160+
>>> self.insert()
161+
>>> return self
162+
156163
.. method:: tasklet.insert()
157164

158165
Insert a tasklet at the end of the scheduler runnables queue, given that it isn't blocked.

Stackless/changelog.txt

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

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

12+
- https://bitbucket.org/stackless-dev/stackless/issues/106
13+
Raise RuntimeError, if you call tasklet.setup() on a tasklet, that is
14+
already alive.
15+
1216
- https://bitbucket.org/stackless-dev/stackless/issues/105
1317
Fix an occasional NULL-pointer access during interpreter shutdown.
1418

Stackless/module/taskletobject.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ tasklet_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
373373
if (t == NULL)
374374
return NULL;
375375
*(int*)&t->flags = 0;
376+
t->recursion_depth = 0;
376377
t->next = NULL;
377378
t->prev = NULL;
378379
t->f.frame = NULL;
@@ -1048,6 +1049,27 @@ tasklet_setup(PyObject *self, PyObject *args, PyObject *kwds)
10481049
{
10491050
PyTaskletObject *task = (PyTaskletObject *) self;
10501051

1052+
if (PyTasklet_Alive(task)) {
1053+
RUNTIME_ERROR("tasklet is alive", NULL);
1054+
}
1055+
1056+
/* The implementation of PyTasklet_Alive does not imply,
1057+
* that the current tasklet is always alive. But I can't figure out,
1058+
* how to create a current tasklet, that is dead.
1059+
*/
1060+
assert(task->cstate->tstate == NULL || task->cstate->tstate->st.current != task);
1061+
1062+
/* guaranted by !alive && !current.
1063+
* Equivalent to the call of tasklet_clear_frames(task) in PyTasklet_BindEx().
1064+
*/
1065+
assert(task->f.frame == NULL);
1066+
1067+
/* the following assertions are equivalent to the remaining argument checks
1068+
* in PyTasklet_BindEx().
1069+
*/
1070+
assert(!PyTasklet_Scheduled(task));
1071+
assert(PyTasklet_GetNestingLevel(task) == 0);
1072+
10511073
if (impl_tasklet_setup(task, args, kwds, 1))
10521074
return NULL;
10531075
Py_INCREF(task);

Stackless/unittests/test_miscell.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,20 @@ def other_thread_main():
11231123
self.assertGreater(tlet.nesting_level, 0)
11241124
self.assertRaisesRegex(RuntimeError, "tasklet has C state on its stack", tlet.bind, None)
11251125

1126+
def test_setup_fail_alive(self):
1127+
# make sure, that you can't bind a tasklet, which is alive
1128+
# https://bitbucket.org/stackless-dev/stackless/issues/106
1129+
1130+
def task():
1131+
t = stackless.current
1132+
t.tempval = lambda: None
1133+
self.assertTrue(t.alive)
1134+
self.assertRaisesRegex(RuntimeError, "tasklet is alive", t.setup)
1135+
1136+
t = stackless.tasklet(task, ())
1137+
t.run()
1138+
self.assertFalse(t.alive)
1139+
11261140

11271141
class TestSwitch(StacklessTestCase):
11281142
"""Test the new tasklet.switch() method, which allows

0 commit comments

Comments
 (0)