Skip to content

Commit ade1fae

Browse files
author
Anselm Kruis
committed
Issue python#121: Fix a reference leak, if Python runs Python code while clearing a thread state
If Python clears a thread state and a destructor or a weakref-callback runs Python code, Python used to create a new initial stub without clearing it later. This change reorders PyThreadState_Clear() to clear the PyStacklessState sub-structure last. A new test case for this problem uses thread local storage to execute a __del__-method. https://bitbucket.org/stackless-dev/stackless/issues/121 (grafted from ede2ad2b32b917d364f523318a82c75d0df2a0e6)
1 parent 119ca9b commit ade1fae

4 files changed

Lines changed: 36 additions & 2 deletions

File tree

Python/pystate.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ void
337337
PyThreadState_Clear(PyThreadState *tstate)
338338
{
339339
#ifdef STACKLESS
340-
STACKLESS_PYSTATE_CLEAR;
340+
slp_kill_tasks_with_stacks(tstate);
341341
#endif
342342
if (Py_VerboseFlag && tstate->frame != NULL)
343343
fprintf(stderr,
@@ -360,6 +360,9 @@ PyThreadState_Clear(PyThreadState *tstate)
360360
tstate->c_tracefunc = NULL;
361361
Py_CLEAR(tstate->c_profileobj);
362362
Py_CLEAR(tstate->c_traceobj);
363+
#ifdef STACKLESS
364+
STACKLESS_PYSTATE_CLEAR;
365+
#endif
363366
}
364367

365368

Stackless/changelog.txt

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

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

12+
- https://bitbucket.org/stackless-dev/stackless/issues/121
13+
Fix a reference leak. If Python clears a thread state and a destructor or a
14+
weakref-callback runs Python code, Python used to leak a reference to a
15+
C-stack object.
16+
1217
- https://bitbucket.org/stackless-dev/stackless/issues/119
1318
Fix a rare bug in the stack unwinding mechanism, that caused a SystemError
1419
exception or an assertion violation, if a __del__()-method or a weakref

Stackless/core/stackless_tstate.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ struct _ts; /* Forward */
114114
void slp_kill_tasks_with_stacks(struct _ts *tstate);
115115

116116
#define __STACKLESS_PYSTATE_CLEAR \
117-
slp_kill_tasks_with_stacks(tstate); \
118117
Py_CLEAR(tstate->st.initial_stub); \
119118
Py_CLEAR(tstate->st.del_post_switch); \
120119
Py_CLEAR(tstate->st.interrupted); \

Stackless/unittests/test_thread.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,33 @@ def test_setup_from_other_thread(self):
616616
theThread.join()
617617

618618

619+
@unittest.skipUnless(withThreads, "requires thread support")
620+
class TestThreadLocalStorage(StacklessTestCase):
621+
class ObjectWithDestructor(object):
622+
def __init__(self, event):
623+
self.event = event
624+
625+
def __del__(self):
626+
self.event.set()
627+
628+
def test_destructor_at_end_of_thread(self):
629+
# Test case for issue #121 https://bitbucket.org/stackless-dev/stackless/issue/121
630+
# Run a destructor during clean up of thread local storage
631+
# Until issue #121 got fixed, this caused a reference leak
632+
tls = threading.local()
633+
deleted = threading.Event()
634+
635+
def other_thread():
636+
tls.owd = self.ObjectWithDestructor(deleted)
637+
638+
self.assertFalse(deleted.is_set())
639+
t = threading.Thread(target=other_thread, name="other thread")
640+
t.start()
641+
t.join()
642+
time.sleep(0.1) # give the thread time to clean up
643+
self.assertTrue(deleted.is_set())
644+
645+
619646
if __name__ == '__main__':
620647
if not sys.argv[1:]:
621648
sys.argv.append('-v')

0 commit comments

Comments
 (0)