Repository navigation
v0.0.53 R02, where the state lives
R02, the second of the nine runtime lessons, and the one that puts a frame around everything before it. A running Python is three nested things: a runtime that there is one of per process, interpreters inside it, and threads inside those. Every fact this book has taught you belongs to exactly one of the three levels, and the level decides who can see a change when you make one.
Eight cells, and every section works the same way: make a second interpreter, ask both of them the same question, and watch for where the answers stop matching. Nine objects asked for their address on both sides. A search for the exact integer at which the two of them stop agreeing. The interpreter list with its ids counting upwards and never coming back. The recursion limit, a warnings filter and sys.modules changed on one side and read on the other. signal.signal tried from the main thread, from another thread and from inside a second interpreter. Two threads each holding a different exception at the same instant.
The facts worth keeping. The shared pile is small and it is not luck: None, the booleans, the small ints, the one character strings, the fixed identifier strings and the static type objects are fields of the runtime struct rather than allocations, which is why nothing can free them and why every interpreter points at the same bytes. Where the small int run ends is a #define, not a policy, and it moved from 256 on 3.14 to 1024 on 3.15, which the cell measures rather than asserts. An interpreter owns its own sys.modules, its own builtins, its own import lock, its own warnings filters and its own recursion limit. Signals are the exception, because the operating system has never heard of interpreters, so there is one handler table per process and the test for who may write to it is two conditions in one line of C. The exception being handled is per thread, which two threads inside an except block at the same instant make obvious.
The lesson also says out loud why no cell calls sys._current_frames from inside a second interpreter: on a build with the GIL that aborts the process, because the function materialises frame objects for other interpreters' frames and each interpreter allocates from its own pools. That is issue #179, found while writing this.
Two Tier 1 recordings, one release and one free threaded. An operating system thread takes a few hundred microseconds and a whole interpreter takes about fifty times that, plus a couple of megabytes of resident memory for as long as you hold it. The free threaded build charges more for both, and the reason is the allocator rather than noise.
Two new glossary terms, runtime state and static object. Six diagrams and fifteen citations. Both READMEs updated. GLOSSARY.md is 218 terms and CLAIMS.md is 571 claims across 73 lessons.