Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-117688: Fix deadlock in test_no_stale_references with GIL disabled #117720

Merged
merged 1 commit into from
Apr 15, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions Lib/test/test_concurrent_futures/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,24 +83,34 @@ def test_no_stale_references(self):
# references.
my_object = MyObject()
my_object_collected = threading.Event()
my_object_callback = weakref.ref(
my_object, lambda obj: my_object_collected.set())
fut = self.executor.submit(my_object.my_method)
def set_event():
if Py_GIL_DISABLED:
# gh-117688 Avoid deadlock by setting the event in a
# background thread. The current thread may be in the middle
# of the my_object_collected.wait() call, which holds locks
# needed by my_object_collected.set().
threading.Thread(target=my_object_collected.set).start()
else:
my_object_collected.set()
my_object_callback = weakref.ref(my_object, lambda obj: set_event())
# Deliberately discarding the future.
self.executor.submit(my_object.my_method)
del my_object

if Py_GIL_DISABLED:
# Due to biased reference counting, my_object might only be
# deallocated while the thread that created it runs -- if the
# thread is paused waiting on an event, it may not merge the
# refcount of the queued object. For that reason, we wait for the
# task to finish (so that it's no longer referenced) and force a
# GC to ensure that it is collected.
fut.result() # Wait for the task to finish.
support.gc_collect()
# refcount of the queued object. For that reason, we alternate
# between running the GC and waiting for the event.
wait_time = 0
collected = False
while not collected and wait_time <= support.SHORT_TIMEOUT:
support.gc_collect()
collected = my_object_collected.wait(timeout=1.0)
wait_time += 1.0
else:
del fut # Deliberately discard the future.

collected = my_object_collected.wait(timeout=support.SHORT_TIMEOUT)
collected = my_object_collected.wait(timeout=support.SHORT_TIMEOUT)
self.assertTrue(collected,
"Stale reference not collected within timeout.")

Expand Down
Loading