Skip to content

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

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
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
69 changes: 69 additions & 0 deletions Doc/reference/executionmodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

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.

Copy link
Member

Choose a reason for hiding this comment

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

This is meant to be implementation agnostic

Oh, oops, didn't see this. Some parts are CPython specific here, though.

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'm trying to avoid any CPython-specific notions here. Do you have an example of another runtime model?

Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

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!

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

Copy link
Contributor

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.

==================

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
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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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."

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

Copy link
Contributor

@jeff5 jeff5 Jul 1, 2025

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

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

capitalization: When

Copy link
Member Author

Choose a reason for hiding this comment

The 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably I'm misreading:

Suggested change
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.
If the runtime supports using multiple interpreters then each OS thread
will have at least one Python thread for each interpreter. However,
only one is active in the OS thread at a time. Switching between
interpreters in the current OS thread means changing the active Python thread.

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

The 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
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy