Skip to content

bpo-42815: Fix issue when thread doesn't copy context of parent thread #24074

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

Closed
wants to merge 6 commits into from

Conversation

uriyyo
Copy link
Member

@uriyyo uriyyo commented Jan 3, 2021

@uriyyo uriyyo requested a review from rhettinger as a code owner January 3, 2021 14:28
@rhettinger rhettinger removed their request for review January 10, 2021 20:02
@github-actions
Copy link

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Feb 10, 2021
# Conflicts:
#	Lib/test/test_threading.py
#	Lib/threading.py
@jcea
Copy link
Member

jcea commented Jul 31, 2021

Could you possibly explain why do you need to amend decimal tests?. I don't understand it.

@jcea
Copy link
Member

jcea commented Jul 31, 2021

Oh, I understand now. You want to preserve the behaviour of a thread messing with decimal context to affect other threads (including main thread).

I find this patch valuable, but the decimal context "interference" in current python could be something production code could depend on. In that situation, this patch is "risky" and we are possibly too late for 3.10, since 3.10rc1 is planned in a couple of days.

Maybe Python 3.11?

We also need a documentation update talking about the new "context" optional parameter.

I requested a review in python-dev.

Copy link
Member

@jcea jcea left a comment

Choose a reason for hiding this comment

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

  1. Test should check that old context is altered when passing the old context as a parameter, and not altered when using an implicit new context.
  2. The same when providing an explicit new context.

@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

Copy link
Member

@vstinner vstinner left a comment

Choose a reason for hiding this comment

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

cc @1st1, author of the contextvars module.

@@ -925,7 +930,7 @@ def run(self):
"""
try:
if self._target is not None:
self._target(*self._args, **self._kwargs)
self._context.run(self._target, *self._args, **self._kwargs)
Copy link
Member

Choose a reason for hiding this comment

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

That's one why to fix the issue. Another would be to modify the contextvars module to detect that it runs in a new thread. For exemple, this change doesn't cover the case of threads spawned by C libraries which then use the Python C API to run Python code. Or functions calling _thread.start_new_thread() directly.

I don't know which option is the best. At least, this change is reasonable small.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry, maybe I add bad description for this issue. I will try to make things more clear. Every thread has it's own context and they do not share same context. This PR tries fix problem when child thread use empty context instead of copy of a parent context.

if context is not None:
self._context = context
else:
self._context = _copy_context()
Copy link
Member

Choose a reason for hiding this comment

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

Is _copy_context() fast/efficient when contextvars is not used at all?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is information from PEP-567:

The Context mapping is implemented using an immutable dictionary. This allows for a O(1) implementation of the copy_context() function.

I believe it is fast/efficient to use copy_context.

@@ -0,0 +1,2 @@
Fix issue when new thread doesn't copy context of a parent thread. Patch
Copy link
Member

Choose a reason for hiding this comment

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

This description is not very helpful. I would prefer to rephrase it to explain that threads no longer share contextvars variables with their parent thread, but the context is now copied.

IMO this change is important enough to be documented in a .. versionchanged:: 3.11 markup in https://docs.python.org/dev/library/threading.html#threading.Thread documentation.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually every thread has it's own context and they do not share same context. This PR tries fix problem when child thread use empty context instead of copy of a parent context.

Copy link
Member

Choose a reason for hiding this comment

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

Please rephrase the NEWS entry and add the requested versionchanged markup.

@uriyyo
Copy link
Member Author

uriyyo commented Aug 10, 2021

@jcea Actually every thread has it's own context and they do not share same context. This PR tries fix problem when child thread use empty context instead of copy of a parent context.

For instead, I can that this problem fixed at asyncio.to_thread:

loop = events.get_running_loop()
ctx = contextvars.copy_context()
func_call = functools.partial(ctx.run, func, *args, **kwargs)
return await loop.run_in_executor(None, func_call)

@jcea
Copy link
Member

jcea commented Aug 10, 2021

@uriyyo , your comment doesn't make sense if a thread modifying its context affects what other threads see. That mean "shared context" in my book :-).

The comment by @vstinner about threads launched by C extensions is interesting. I was thinking about detecting this case when getting the GIL and copy the context at that time. The problem would be to decide which context to copy, in the general case.

Maybe perfection is enemy of good and this PR would be good enough for now.

@vstinner
Copy link
Member

Currently, the _contextvars module is built as a dynamic library, it is not available when setup.py build is run by make to build Python, and so import logging fails when trying to get it indirectly.

I suggest to modify Modules/Setup to uncomment #_contextvars (...), so the _contextvars module is built as a built-in module, and so is always available.

@github-actions github-actions bot removed the stale Stale PR or inactive for long period of time. label Aug 2, 2022
@uriyyo
Copy link
Member Author

uriyyo commented Dec 13, 2022

Closing this PR as far as a related issue has been closed: #86981 (comment)

@uriyyo uriyyo closed this Dec 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants