-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
gh-135944: Add a "Runtime Components" Section to the Execution Model Docs #135945
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
535c8b8
3f3d5cc
b1d6ed7
aeca87a
b12a02b
17a2f34
9ac4b4a
f7cb965
e71394c
8f454c4
cd0200c
9ccc743
4dce0fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -398,6 +398,75 @@ See also the description of the :keyword:`try` statement in section :ref:`try` | |||||||||||||||||
and :keyword:`raise` statement in section :ref:`raise`. | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
.. _execcomponents: | ||||||||||||||||||
|
||||||||||||||||||
Runtime Components | ||||||||||||||||||
================== | ||||||||||||||||||
|
||||||||||||||||||
Python's execution model does not operate in a vacuum. It runs on a | ||||||||||||||||||
computer. When a program runs, the conceptual layers of how it runs | ||||||||||||||||||
on the computer look something like this:: | ||||||||||||||||||
|
||||||||||||||||||
host computer (or VM or container) | ||||||||||||||||||
process | ||||||||||||||||||
OS thread (runs machine code) | ||||||||||||||||||
|
||||||||||||||||||
.. (Sometimes there may even be an extra layer right after "thread" | ||||||||||||||||||
for light-weight threads or coroutines.) | ||||||||||||||||||
|
||||||||||||||||||
While a program always starts with exactly one of each of those, it may | ||||||||||||||||||
grow to include multiple of each. Hosts and processes are isolated and | ||||||||||||||||||
independent from one another. However, threads are not. Each thread | ||||||||||||||||||
ZeroIntensity marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
does *run* independently, for the small segments of time it is | ||||||||||||||||||
scheduled to execute its code on the CPU. Otherwise, all threads | ||||||||||||||||||
in a process share all the process' resources, including memory. | ||||||||||||||||||
This is exactly what can make threads a pain: two threads running | ||||||||||||||||||
at the same arbitrary time on different CPU cores can accidentally | ||||||||||||||||||
interfere with each other's use of some shared data. The initial | ||||||||||||||||||
thread is known as the "main" thread. | ||||||||||||||||||
|
||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This sounds a bit like the risk of accidents is unavoidable. Though I take your point, some science is available, so I offer: "The care needed to co-ordinate access to intentionally shared data, when two or more threads could be running, is exactly what makes threads a painful way to exploit the potential concurrence of multiple CPUs." There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Arguably, the risk of accidents with threads is unavoidable. 😄 That said, I'll clarify similarly to what you've suggested. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I've driven you to use a lot of words. I'm not as pessinistic as this about threads, but won't claim correctness comes easy. You're the editor. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's okay. I tend toward verbosity. 😄 In this case, I guess I've spent so much time over the last 5-7 years dealing with thread-related bugs that my perspective has been affected aversely. I end up wanting to shout warnings from the roof tops. |
||||||||||||||||||
The same layers apply to each Python program, with some extra layers | ||||||||||||||||||
specific to Python:: | ||||||||||||||||||
|
||||||||||||||||||
host | ||||||||||||||||||
process | ||||||||||||||||||
Python runtime | ||||||||||||||||||
interpreter | ||||||||||||||||||
Python thread (runs bytecode) | ||||||||||||||||||
|
||||||||||||||||||
when a Python program starts, it looks exactly like that, with one | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. capitalization: When There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||||||||||||||
of each. The process has a single global runtime to manage global | ||||||||||||||||||
resources. Each Python thread has all the state it needs to run | ||||||||||||||||||
Python code (and use any supported C-API) in its OS thread. | ||||||||||||||||||
|
||||||||||||||||||
.. , including its stack of call frames. | ||||||||||||||||||
|
||||||||||||||||||
.. If the program uses coroutines (async) then the thread will end up | ||||||||||||||||||
juggling multiple stacks. | ||||||||||||||||||
|
||||||||||||||||||
In between the global runtime and the threads lies the interpreter. | ||||||||||||||||||
It encapsulates all of the non-global runtime state that the | ||||||||||||||||||
interpreter's Python threads share. For example, all those threads | ||||||||||||||||||
share :data:`sys.modules`. When a Python thread is created, it belongs | ||||||||||||||||||
to an interpreter. | ||||||||||||||||||
|
||||||||||||||||||
If the runtime supports using multiple interpreters then each OS thread | ||||||||||||||||||
will have at most one Python thread for each interpreter. However, | ||||||||||||||||||
only one is active in the OS thread at a time. Switching between | ||||||||||||||||||
interpreters means changing the active Python thread. | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably I'm misreading:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's "at most" because there may be zero or one Python thread per thread per interpreter. However, that isn't strictly true, since you can create as many thread states as you want. Only one can be active at a time for a give thread and each one is strictly bound to a single thread. I'll tweak the wording. Thanks for pointing this out. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||||||||||||||
The initial interpreter is known as the "main" interpreter. | ||||||||||||||||||
|
||||||||||||||||||
.. (The interpreter is different from the "bytecode interpreter", | ||||||||||||||||||
of which each thread has one to execute Python code.) | ||||||||||||||||||
|
||||||||||||||||||
Once a program is running, new Python threads can be created using the | ||||||||||||||||||
:mod:`threading` module. Additional processes can be created using the | ||||||||||||||||||
:mod:`multiprocessing` and :mod:`subprocess` modules. You can run | ||||||||||||||||||
coroutines (async) in the main thread using :mod:`asyncio`. | ||||||||||||||||||
Interpreters can be created using the :mod:`concurrent.interpreters` | ||||||||||||||||||
module. | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
.. rubric:: Footnotes | ||||||||||||||||||
|
||||||||||||||||||
.. [#] This limitation occurs because the code that is executed by these operations | ||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be a good idea to mention that this is CPython specific. Other implementations are allowed to have different runtime models.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, oops, didn't see this. Some parts are CPython specific here, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to avoid any CPython-specific notions here. Do you have an example of another runtime model?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I'm not familiar with how any other implementations work, this is coming solely off a bit of speculation on my part. My concern is that a thread is inherently CPython-specific, because some Python implementations exist in areas that don't have access to OS threads, such as Brython for JS, and probably MicroPython/CircuitPython for some microcontrollers.
I do think that this is a great section to have for subinterpreters, I just think it'd be better to keep some parts of it CPython specific. Does that make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a conceptual standpoint, which is the level of this text, threads are not implementation-specific (or platform-specific): the Python runtime always deals with at least the one thread in which it was started. Not all platforms and implementations support multiple threads, but that's a different matter. I'll add a sentence clarifying that point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I think I can get behind that. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the question of implementations that do not have access to OS threads, unless they are unable to create Thread objects, I think this is covered by the possibility of multiple Python threads may be mapped to the single OS thread available. I'm assuming this is multiple Python threads in one interpreter, but interested to be reassured about the multiplicity of the relationships in this data model. I'm reading "OS thread" as "JVM Thread" for my purposes. It's not necessarily the same, but I don't think we can/should peek behind that particular abstraction.