Skip to content

bpo-24484: Avoid race condition in multiprocessing cleanup #2159

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

Merged
merged 4 commits into from
Jun 13, 2017

Conversation

pitrou
Copy link
Member

@pitrou pitrou commented Jun 13, 2017

The finalizer registry can be mutated while inspected by multiprocessing at process exit.

The finalizer registry can be mutated while inspected by multiprocessing
at process exit.
@pitrou pitrou added needs backport to 2.7 type-bug An unexpected behavior, bug, or error labels Jun 13, 2017
@pitrou pitrou requested a review from serhiy-storchaka June 13, 2017 10:32
Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general LGTM, but I have several questions.


# list(_finalizer_registry) should be atomic, while
# list(_finalizer_registry.items()) is not.
keys = [key for key in list(_finalizer_registry) if f(key)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be written also as

keys = list(_finalizer_registry)
keys = sorted(filter(f, keys), reverse=True)

if you prefer.

@@ -3110,6 +3110,14 @@ class _TestFinalize(BaseTestCase):

ALLOWED_TYPES = ('processes',)

def setUp(self):
self.registry_backup = util._finalizer_registry.copy()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed for every test or just for test_thread_safety?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for test_thread_safety, but it was easier to put it here.

threads.append(t)
time.sleep(4.0) # Wait a bit to trigger race condition
finish = True
for t in threads:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use test.support.start_threads() for running and finalizing threads?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I will take a look.

def make_finalizers():
nonlocal exc
d = {}
threshold = 60
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's a leftover from a previous version of the test :-) Will remove.


def run_finalizers():
nonlocal exc
while not finish:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add and not exc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, perhaps.

try:
# Old Foo's get gradually replaced and later
# collected by the GC (because of the cyclic ref)
d[random.getrandbits(5)] = {Foo() for i in range(10)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a set?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured it would make deallocation order less deterministic than a list.

while not finish:
try:
# Old Foo's get gradually replaced and later
# collected by the GC (because of the cyclic ref)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering how much objects can create a fast interpreter (e.g. PyPy) on fast computer before hitting a bug or exhausting the test time.

# is running (either by a GC run or by another thread).

# list(_finalizer_registry) should be atomic, while
# list(_finalizer_registry.items()) is not.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it is possible to make list(dict.items()) almost atomic (except allocating new tuples) or even truly atomic. Is it worth?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a separate PR if you like, but this fix needs to be backported anyway.

Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Older versions of Python may need different fix or test due to different implementations of dict and finalization.

@pitrou
Copy link
Member Author

pitrou commented Jun 13, 2017

Older versions of Python may need different fix or test due to different implementations of dict and finalization.

That's quite likely. I can't think of a universal solution that wouldn't add a ton of complication, though.
(one possible complication would be to use one of the WeakDicts, which already has sophisticated logic to avoid race conditions)

@pitrou
Copy link
Member Author

pitrou commented Jun 13, 2017

I'm gonna merge this and see how the backports go.

@pitrou pitrou merged commit 1eb6c00 into python:master Jun 13, 2017
@pitrou pitrou deleted the run_finalizers_race branch June 13, 2017 15:10
pitrou added a commit to pitrou/cpython that referenced this pull request Jun 13, 2017
…honGH-2159)

* bpo-24484: Avoid race condition in multiprocessing cleanup

The finalizer registry can be mutated while inspected by multiprocessing
at process exit.

* Use test.support.start_threads()

* Add Misc/NEWS.
(cherry picked from commit 1eb6c00)
pitrou added a commit to pitrou/cpython that referenced this pull request Jun 13, 2017
…honGH-2159)

* bpo-24484: Avoid race condition in multiprocessing cleanup

The finalizer registry can be mutated while inspected by multiprocessing
at process exit.

* Use test.support.start_threads()

* Add Misc/NEWS.
(cherry picked from commit 1eb6c00)
pitrou added a commit to pitrou/cpython that referenced this pull request Jun 13, 2017
…honGH-2159)

* bpo-24484: Avoid race condition in multiprocessing cleanup

The finalizer registry can be mutated while inspected by multiprocessing
at process exit.

* Use test.support.start_threads()

* Add Misc/NEWS.
(cherry picked from commit 1eb6c00)
pitrou added a commit that referenced this pull request Jun 13, 2017
…2159) (#2166)

* bpo-24484: Avoid race condition in multiprocessing cleanup

The finalizer registry can be mutated while inspected by multiprocessing
at process exit.

* Use test.support.start_threads()

* Add Misc/NEWS.
(cherry picked from commit 1eb6c00)
pitrou added a commit that referenced this pull request Jun 13, 2017
…2159) (#2168)

* bpo-24484: Avoid race condition in multiprocessing cleanup

The finalizer registry can be mutated while inspected by multiprocessing
at process exit.

* Use test.support.start_threads()

* Add Misc/NEWS.
(cherry picked from commit 1eb6c00)
pitrou added a commit that referenced this pull request Jun 13, 2017
…2159) (#2167)

* bpo-24484: Avoid race condition in multiprocessing cleanup

The finalizer registry can be mutated while inspected by multiprocessing
at process exit.

* Use test.support.start_threads()

* Add Misc/NEWS.
(cherry picked from commit 1eb6c00)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants