Skip to content

Commit 5c4a74b

Browse files
author
Anselm Kruis
committed
Stackless issue python#90: raise RuntimeError on unbinding main tasklets.
Unbinding (tasklet.bind(None) ) of main tasklets caused an assertion violation. Now it raises RuntimError. https://bitbucket.org/stackless-dev/stackless/issues/90 (grafted from 58b930a8a1d931da2f5bc834681af5d1452e1733 and 46335aa61c7c)
1 parent baab088 commit 5c4a74b

3 files changed

Lines changed: 60 additions & 0 deletions

File tree

Stackless/changelog.txt

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

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

13+
- https://bitbucket.org/stackless-dev/stackless/issues/90
14+
Stackless now raises a RuntimeError, if you try to unbind (tlet.bind(None))
15+
a main-tasklet.
16+
1317
- https://bitbucket.org/stackless-dev/stackless/issues/78
1418
Documentation update: if you iterate over a channel, the
1519
sender must send StopIteration manually. See

Stackless/module/taskletobject.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ PyTasklet_BindEx(PyTaskletObject *task, PyObject *func, PyObject *args, PyObject
251251
if (tasklet_has_c_stack(task)) {
252252
RUNTIME_ERROR("tasklet has C state on its stack", -1);
253253
}
254+
if (ts && task == ts->st.main && args == NULL && kwargs == NULL) {
255+
RUNTIME_ERROR("can't unbind the main tasklet", -1);
256+
}
254257

255258
tasklet_clear_frames(task);
256259
assert(task->f.frame == NULL);

Stackless/unittests/test_miscell.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import contextlib
99
import weakref
1010
import types
11+
import _thread as thread
1112

1213
from support import StacklessTestCase, AsTaskletTestCase
1314
try:
@@ -876,6 +877,58 @@ def test_bind_args_not_runnable(self):
876877
self.assertFalse(t.scheduled)
877878
t.run()
878879

880+
def test_unbind_main(self):
881+
self.skipUnlessSoftswitching()
882+
883+
done = []
884+
885+
def other():
886+
main = stackless.main
887+
self.assertRaisesRegex(RuntimeError, "can't unbind the main tasklet", main.bind, None)
888+
889+
# the initial nesting level depends on the test runner.
890+
# We need a main tasklet with nesting_level == 0. Therefore we
891+
# use a thread
892+
def other_thread():
893+
self.assertEqual(stackless.current.nesting_level, 0)
894+
self.assertIs(stackless.current, stackless.main)
895+
stackless.tasklet(other)().switch()
896+
done.append(True)
897+
898+
t = threading.Thread(target=other_thread, name="other thread")
899+
t.start()
900+
t.join()
901+
self.assertTrue(done[0])
902+
903+
def test_rebind_main(self):
904+
# rebind the main tasklet of a thread. This is highly discouraged,
905+
# because it will deadlock, if the thread is a threading.Thread.
906+
self.skipUnlessSoftswitching()
907+
908+
ready = thread.allocate_lock()
909+
ready.acquire()
910+
911+
self.target_called = False
912+
self.main_returned = False
913+
914+
def target():
915+
self.target_called = True
916+
ready.release()
917+
918+
def other_thread_main():
919+
self.assertTrue(stackless.current.is_main)
920+
try:
921+
stackless.tasklet(stackless.main.bind)(target, ()).switch()
922+
finally:
923+
self.main_returned = True
924+
ready.release()
925+
926+
thread.start_new_thread(other_thread_main, ())
927+
ready.acquire()
928+
929+
self.assertTrue(self.target_called)
930+
self.assertFalse(self.main_returned)
931+
879932

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

0 commit comments

Comments
 (0)