Skip to content

Commit a0f56e7

Browse files
author
Anselm Kruis
committed
Stackless issue python#222: add a test case
Add a test case for a main tasklet, that terminates with serial_last_jump != initial_stub.serial.
1 parent 0b8da63 commit a0f56e7

File tree

1 file changed

+86
-1
lines changed

1 file changed

+86
-1
lines changed

Stackless/unittests/test_outside.py

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
from __future__ import absolute_import
2+
23
import unittest
4+
import threading
5+
import stackless
36
from stackless import test_cframe_nr, test_outside
47
from stackless import tasklet, channel, run
58
from _teststackless import test_cframe, test_cstate
69

710
from support import test_main # @UnusedImport
8-
from support import StacklessTestCase
11+
from support import StacklessTestCase, withThreads, get_serial_last_jump
12+
13+
14+
def current_initial_stub(threadid=-1):
15+
"""Get the initial stub of the given thread.
16+
"""
17+
# The second argument of get_thread_info() is intentionally undocumented.
18+
# See C source.
19+
return stackless.get_thread_info(threadid, 1 << 30)[5]
20+
21+
22+
MAIN_INITIAL_STUB = current_initial_stub()
923

1024

1125
class TestOutside(StacklessTestCase):
@@ -70,6 +84,77 @@ def test_outside5(self):
7084
tasklet(test_cframe_nr)(100)
7185
test_outside()
7286

87+
@unittest.skipUnless(withThreads, "requires thread support")
88+
def test_main_exit_with_serial_mismatch(self):
89+
# Test the exit of a main-tasklet with a current C-stack serial number
90+
# unequal to the serial number of the initial stub.
91+
# The purpose of this test is to exercise a special code path in
92+
# slp_tasklet_end(PyObject *retval), scheduling.c:1444
93+
94+
# Note: this test only works in thread for two reasons:
95+
# - The main tasklet terminates
96+
# - The sequence of operations used to change the serial of the
97+
# main tasklet depends on the fact, that the thread state is
98+
# not the initial thread state of the interpreter.
99+
100+
# sanity checks
101+
self.assertEqual(get_serial_last_jump(), MAIN_INITIAL_STUB.serial)
102+
self.result = None
103+
104+
def run():
105+
try:
106+
# Test preconditions: current is main at level 0
107+
if stackless.enable_softswitch(None):
108+
self.assertEqual(stackless.current.nesting_level, 0)
109+
self.assertIs(stackless.current, stackless.main)
110+
thread_initial_stub = current_initial_stub()
111+
# sanity check
112+
self.assertEqual(get_serial_last_jump(), thread_initial_stub.serial)
113+
114+
# a tasklet with a different C-stack
115+
# test_cstate forces a hard switch from schedule_remove
116+
t = tasklet(test_cstate)(stackless.schedule_remove)
117+
t_cstate = t.cstate
118+
119+
# now t has a cstate, that belongs to thread_initial_stub
120+
# check it
121+
self.assertIsNot(t_cstate, thread_initial_stub)
122+
self.assertEqual(t_cstate.serial, thread_initial_stub.serial)
123+
124+
# Run t from a new entry point.
125+
# Hard switch to t,
126+
# run t and
127+
# hard switch back to the main tasklet
128+
test_outside() # run scheduled tasklets
129+
self.assertEqual(t.nesting_level, 1) # It was a hard switch back to main
130+
131+
# t has now it's own stack created from a different entry point
132+
self.assertIsNot(t.cstate, t_cstate)
133+
self.assertNotEqual(t.cstate.serial, thread_initial_stub.serial)
134+
t_serial = t.cstate.serial
135+
136+
# soft-to-hard switch to t, finish t and soft-switch back
137+
t.run()
138+
139+
self.assertEqual(t.nesting_level, 0) # Soft switching was possible
140+
if stackless.enable_softswitch(None):
141+
# Final test: the current serial has changed.
142+
self.assertNotEqual(get_serial_last_jump(), thread_initial_stub.serial)
143+
self.assertEqual(get_serial_last_jump(), t_serial)
144+
else:
145+
self.assertEqual(get_serial_last_jump(), thread_initial_stub.serial)
146+
except Exception as e:
147+
self.result = e
148+
else:
149+
self.result = False
150+
151+
thread = threading.Thread(target=run, name=str(self.id()))
152+
thread.start()
153+
thread.join()
154+
if self.result:
155+
raise self.result
156+
self.assertIs(self.result, False)
157+
73158

74159
class TestCframe(StacklessTestCase):
75160
n = 100

0 commit comments

Comments
 (0)