From b804d741d1b11c706fc13ed274807e6ef91f4085 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 27 Dec 2021 16:45:39 -0700 Subject: [PATCH 01/10] Frame out the PEP. --- pep-0679.rst | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 pep-0679.rst diff --git a/pep-0679.rst b/pep-0679.rst new file mode 100644 index 00000000000..de5ddd2610d --- /dev/null +++ b/pep-0679.rst @@ -0,0 +1,139 @@ +PEP: 679 +Title: A Per-Interpreter GIL +Author: Eric Snow +PEP-Delegate: +Discussions-To: python-dev@python.org +Status: Draft +Type: Informational +Created: 01-01-2022 +Python-Version: 3.11 +Post-History: 01-01-2022 +Resolution: + + +Abstract +======== + +CPython has supported multiple interpreters in the same process (AKA +"subinterpreters") since version 1.5 (1997). The feature has been +available via the C-API. [c-api]_ PEP 554 discusses some of the value of +subinterpreters and the merits of exposing them to Python code. +However, that PEP purposefully avoids discussion about isolation, +especially related to the GIL. This PEP fills that role. + +The more isolation there is between interpreters, the more value they +can offer. Currently subinterpreters operate in +`relative isolation from one another `_. If they +were fully isolated then they could operate in parallel on multi-core +hosts. + +This proposal identifies a path forward to reach full isolation between +interpreters. This includes making the GIL per-interpreter. + + +Proposal +======== + +TBD + + +Motivation +========== + +[Clearly explain why the existing language specification is inadequate to address the problem that the PEP solves.] + + +Rationale +========= + +[Describe why particular design decisions were made.] + +Concerns +-------- + +TBD + + +Specification +============= + +[Describe the syntax and semantics of any new language feature.] + + +Backwards Compatibility +======================= + +[Describe potential impact and severity on pre-existing code.] + + +Alternate Python Implementations +================================ + +(not affected? this is CPython-only) + + +Security Implications +===================== + +[How could a malicious user take advantage of this new feature?] + + +How to Teach This +================= + +[How to teach users, new and experienced, how to apply the PEP to their work.] + + +Documentation +============= + +TBD + + +Deferred Functionality +====================== + +TBD + + +Reference Implementation +======================== + +[Link to any existing implementation and details about its state, e.g. proof-of-concept.] + + +Rejected Ideas +============== + +[Why certain ideas that were brought while discussing this PEP were not ultimately pursued.] + + +Open Issues +=========== + +[Any points that are still being decided/discussed.] + + +References +========== + +.. [c-api] + https://docs.python.org/3/c-api/init.html#sub-interpreter-support + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. + + + +.. + Local Variables: + mode: indented-text + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + coding: utf-8 + End: From 2f46d86b7e50a0cf19c33c92d627ca0a2f95b152 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 27 Dec 2021 16:46:34 -0700 Subject: [PATCH 02/10] Copy over some text from PEP 554. --- pep-0679.rst | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/pep-0679.rst b/pep-0679.rst index de5ddd2610d..6acac611cf4 100644 --- a/pep-0679.rst +++ b/pep-0679.rst @@ -60,6 +60,135 @@ Specification [Describe the syntax and semantics of any new language feature.] +About Subinterpreters +===================== + +(copied from PEP 554, needs editing) + +Concurrency +----------- + +Concurrency is a challenging area of software development. Decades of +research and practice have led to a wide variety of concurrency models, +each with different goals. Most center on correctness and usability. + +One class of concurrency models focuses on isolated threads of +execution that interoperate through some message passing scheme. A +notable example is `Communicating Sequential Processes`_ (CSP) (upon +which Go's concurrency is roughly based). The isolation inherent to +subinterpreters makes them well-suited to this approach. + +Shared data +----------- + +Subinterpreters are inherently isolated (with caveats explained below), +in contrast to threads. So the same communicate-via-shared-memory +approach doesn't work. Without an alternative, effective use of +concurrency via subinterpreters is significantly limited. + +The key challenge here is that sharing objects between interpreters +faces complexity due to various constraints on object ownership, +visibility, and mutability. At a conceptual level it's easier to +reason about concurrency when objects only exist in one interpreter +at a time. At a technical level, CPython's current memory model +limits how Python *objects* may be shared safely between interpreters; +effectively objects are bound to the interpreter in which they were +created. Furthermore, the complexity of *object* sharing increases as +subinterpreters become more isolated, e.g. after GIL removal. + +Consequently,the mechanism for sharing needs to be carefully considered. +There are a number of valid solutions, several of which may be +appropriate to support in Python. This proposal provides a single basic +solution: "channels". Ultimately, any other solution will look similar +to the proposed one, which will set the precedent. Note that the +implementation of ``Interpreter.run()`` will be done in a way that +allows for multiple solutions to coexist, but doing so is not +technically a part of the proposal here. + +Regarding the proposed solution, "channels", it is a basic, opt-in data +sharing mechanism that draws inspiration from pipes, queues, and CSP's +channels. [fifo]_ + +As simply described earlier by the API summary, +channels have two operations: send and receive. A key characteristic +of those operations is that channels transmit data derived from Python +objects rather than the objects themselves. When objects are sent, +their data is extracted. When the "object" is received in the other +interpreter, the data is converted back into an object owned by that +interpreter. + +To make this work, the mutable shared state will be managed by the +Python runtime, not by any of the interpreters. Initially we will +support only one type of objects for shared state: the channels provided +by ``create_channel()``. Channels, in turn, will carefully manage +passing objects between interpreters. + +This approach, including keeping the API minimal, helps us avoid further +exposing any underlying complexity to Python users. Along those same +lines, we will initially restrict the types that may be passed through +channels to the following: + +* None +* bytes +* str +* int +* channels + +Limiting the initial shareable types is a practical matter, reducing +the potential complexity of the initial implementation. There are a +number of strategies we may pursue in the future to expand supported +objects and object sharing strategies. + +Interpreter Isolation +--------------------- + +CPython's interpreters are intended to be strictly isolated from each +other. Each interpreter has its own copy of all modules, classes, +functions, and variables. The same applies to state in C, including in +extension modules. The CPython C-API docs explain more. [caveats]_ + +However, there are ways in which interpreters share some state. First +of all, some process-global state remains shared: + +* file descriptors +* builtin types (e.g. dict, bytes) +* singletons (e.g. None) +* underlying static module data (e.g. functions) for + builtin/extension/frozen modules + +There are no plans to change this. + +Second, some isolation is faulty due to bugs or implementations that did +not take subinterpreters into account. This includes things like +extension modules that rely on C globals. [cryptography]_ In these +cases bugs should be opened (some are already): + +* readline module hook functions (http://bugs.python.org/issue4202) +* memory leaks on re-init (http://bugs.python.org/issue21387) + +Finally, some potential isolation is missing due to the current design +of CPython. Improvements are currently going on to address gaps in this +area: + +* GC is not run per-interpreter [global-gc]_ +* at-exit handlers are not run per-interpreter [global-atexit]_ +* extensions using the ``PyGILState_*`` API are incompatible [gilstate]_ +* interpreters share memory management (e.g. allocators, gc) +* interpreters share the GIL + +Existing Usage +-------------- + +Subinterpreters are not a widely used feature. In fact, the only +documented cases of widespread usage are +`mod_wsgi `_, +`OpenStack Ceph `_, and +`JEP `_. On the one hand, these cases +provide confidence that existing subinterpreter support is relatively +stable. On the other hand, there isn't much of a sample size from which +to judge the utility of the feature. + + Backwards Compatibility ======================= @@ -120,6 +249,48 @@ References .. [c-api] https://docs.python.org/3/c-api/init.html#sub-interpreter-support +.. [caveats] + https://docs.python.org/3/c-api/init.html#bugs-and-caveats + +.. [petr-c-ext] + https://mail.python.org/pipermail/import-sig/2016-June/001062.html + https://mail.python.org/pipermail/python-ideas/2016-April/039748.html + +.. [cryptography] + https://github.com/pyca/cryptography/issues/2299 + +.. [global-gc] + http://bugs.python.org/issue24554 + +.. [gilstate] + https://bugs.python.org/issue10915 + http://bugs.python.org/issue15751 + +.. [global-atexit] + https://bugs.python.org/issue6531 + +.. [bug-rate] + https://mail.python.org/pipermail/python-ideas/2017-September/047094.html + +.. [benefits] + https://mail.python.org/pipermail/python-ideas/2017-September/047122.html + +.. [main-thread] + https://mail.python.org/pipermail/python-ideas/2017-September/047144.html + https://mail.python.org/pipermail/python-dev/2017-September/149566.html + +.. [reset_globals] + https://mail.python.org/pipermail/python-dev/2017-September/149545.html + +.. [multi-core-project] + https://github.com/ericsnowcurrently/multi-core-python + +.. [cache-line-ping-pong] + https://mail.python.org/archives/list/python-dev@python.org/message/3HVRFWHDMWPNR367GXBILZ4JJAUQ2STZ/ + +.. [extension-docs] + https://docs.python.org/3/extending/index.html + Copyright ========= From d55043ccb7616d0f8aaab71ee97bb2bdccb9d8af Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 27 Dec 2021 16:47:05 -0700 Subject: [PATCH 03/10] Note relevant issues and email threads. --- pep-0679.rst | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/pep-0679.rst b/pep-0679.rst index 6acac611cf4..4946ec756de 100644 --- a/pep-0679.rst +++ b/pep-0679.rst @@ -292,6 +292,175 @@ References https://docs.python.org/3/extending/index.html +PEP 384 -- Defining a Stable ABI, which added C API for creating heap types +PEP 432 -- Simplifying the CPython startup sequence +PEP 489 -- Multi-phase extension module initialization +PEP 573 -- Module State Access from C Extension Methods +PEP 630 -- Isolating Extension Modules +PEP 3121 -- ... + +https://bugs.python.org/issue40512 [subinterpreters] Meta issue: per-interpreter GIL +https://bugs.python.org/issue45953 Statically allocate interpreter states as much as possible. + +globals: +https://bugs.python.org/issue36876 [subinterpreters] Global C variables are a problem +https://bugs.python.org/issue45887 [subinterpreters] Pull all interpreter-global objects into one place. +https://bugs.python.org/issue46006 [subinterpreter] _PyUnicode_EqualToASCIIId() issue with subinterpreters +https://bugs.python.org/issue41692 Deprecate immortal interned strings: PyUnicode_InternImmortal() + +interpreter isolation: +https://bugs.python.org/issue40533 [subinterpreters] Don't share Python objects between interpreters +https://bugs.python.org/issue39376 Avoid modifying the process global environment (not thread safe) +https://bugs.python.org/issue40521 ~ [subinterpreters] Make free lists and unicode caches per-interpreter +https://bugs.python.org/issue39511 [subinterpreters] Per-interpreter singletons (None, True, False, etc.) +https://bugs.python.org/issue40522 [subinterpreters] Get the current Python interpreter state from Thread Local Storage (autoTSSkey) +https://bugs.python.org/issue43313 feature: support pymalloc for subinterpreters. each subinterpreter has pymalloc_state + +stdlib isolation: +https://bugs.python.org/issue40077 Convert static types to heap types: use PyType_FromSpec() +https://bugs.python.org/issue42972 [C API] Heap types (PyType_FromSpec) must fully implement the GC protocol +https://bugs.python.org/issue15870 PyType_FromSpec should take metaclass as an argument +https://bugs.python.org/issue45113 [subinterpreters][C API] Add a new function to create PyStructSequence from Heap. + +possible restrictions: +https://bugs.python.org/issue40234 [subinterpreters] Disallow daemon threads in subinterpreters optionally +https://bugs.python.org/issue38435 Start the deprecation cycle for subprocess preexec_fn +https://bugs.python.org/issue42969 pthread_exit & PyThread_exit_thread from PyEval_RestoreThread etc. are harmful +https://bugs.python.org/issue40453 ~ [subinterpreters] Add PyConfig._isolated_interpreter: isolated subinterpreters +https://bugs.python.org/issue42346 [subinterpreters] Deny os.fork() in subinterpreters? +https://bugs.python.org/issue38865 [subinterpreters] Can Py_Finalize() be called if the current interpreter is not the main interpreter? + +C-API objects: +https://bugs.python.org/issue40601 [C API] Hide static types from the limited C API +https://bugs.python.org/issue43503 [subinterpreters] PyObject statics exposed in the limited API break isolation. +https://bugs.python.org/issue43442 multicorevm: guarantee type multi sub interpreters safe + +immortal objects: +https://bugs.python.org/issue40255 Fixing Copy on Writes from reference counting + +extension module isolation: +https://bugs.python.org/issue34309 Trouble when reloading extension modules. +https://bugs.python.org/issue32973 Importing the same extension module under multiple names breaks non-reinitialisable extension modules + +finalization bugs: +https://bugs.python.org/issue6642 returning after forking a child thread doesn't call Py_Finalize +https://bugs.python.org/issue36476 Runtime finalization assumes all other threads have exited. +https://bugs.python.org/issue36780 Interpreter exit blocks waiting for futures of shut-down ThreadPoolExecutors +https://bugs.python.org/issue42647 Unable to use concurrent.futures in atexit hook +https://bugs.python.org/issue43944 Processes in Python 3.9 exiting with code 1 when It's created inside a ThreadPoolExecutor +https://bugs.python.org/issue43588 [Subinterpreters]: use static variable under building Python with --with-experimental-isolated-subinterpreters cause crash. +https://bugs.python.org/issue44100 test__xxsubinterpreters: test_one() fails in AMD64 Fedora Stable 3.x: "Fatal Python error: Py_EndInterpreter: thread still has a frame" +https://bugs.python.org/issue36225 [subinterpreters] Lingering subinterpreters should be implicitly cleared on shutdown + +isolation bugs: +https://bugs.python.org/issue4202 [subinterpreters] Multiple interpreters and readline module hook functions. +https://bugs.python.org/issue10915 ~ [subinterpreters] Make the PyGILState API compatible with multiple interpreters +https://bugs.python.org/issue15751 [subinterpreters] Make the PyGILState API compatible with subinterpreters +https://bugs.python.org/issue24554 ~ [subinterpreters] GC should happen when a subinterpreter is destroyed +https://bugs.python.org/issue31517 MainThread association logic is fragile +https://bugs.python.org/issue39042 Use the runtime's main thread ID in the threading module. +https://bugs.python.org/issue40231 [subinterpreters] Fix pending calls in subinterpreters +https://bugs.python.org/issue40082 ~ trip_signal() gets NULL tstate on Windows on CTRL+C +https://bugs.python.org/issue44532 multi subinterpreters use _PyStructSequence_InitType failed. +https://bugs.python.org/issue46070 _PyImport_FixupExtensionObject() regression causing a crash in subintepreters +https://bugs.python.org/issue46036 Single-phase initialized modules gets initialized multiple times in 3.10.0 + +other bugs: +https://bugs.python.org/issue44374 PyThreadState_IsCurrent bug under building Python with --with-experimental-isolated-subinterpreters + +other (mine): +https://bugs.python.org/issue24553 [subinterpreters] Improve test coverage for subinterpreters +https://bugs.python.org/issue33607 [subinterpreters] Explicitly track object ownership (and allocator). + +PEP 554: +https://bugs.python.org/issue40572 [subinterpreters] Support basic asynchronous cross-interpreter operations. +https://bugs.python.org/issue33608 Add a cross-interpreter-safe mechanism to indicate that an object may be destroyed. +https://bugs.python.org/issue35813 shared memory construct to avoid need for serialization between processes +https://bugs.python.org/issue37293 concurrent.futures.InterpreterPoolExecutor + + +2005-06 https://mail.python.org/archives/list/python-dev@python.org/thread/B5JSYFLBIVKY4QXPBIUNUFG5OMGJX2CZ/#N3GI5BMQM3KQY7G5K3VAAIOC3V7QF2L3 + prioritize +2006-07 https://mail.python.org/archives/list/python-dev@python.org/thread/FNCZEX6PFCNJMIBPHSWHP4SSMP3A24HL/ + not strong isolation +2008-09 https://mail.python.org/archives/list/capi-sig@python.org/thread/UK4H6SRAEWIJM63VWBI724D2W7KYQLD6/#QYEUP6RRPCEMSOGDXD7YMIWKYBH32PUJ + not better than subprocesses (yet) +2008-12 https://mail.python.org/archives/list/python-dev@python.org/thread/GJC53OSY3IH7IGOTSLBIXMPDSUKAT2DL + a replacement for threads? +2009-07 https://mail.python.org/archives/list/python-ideas@python.org/thread/Y6DKIWDCNBGI6ZSTY2W4UDB5PYHQELAS/#UJ6WV4DQ455X7JFF77QUXFUJN6OEJZPE + shared objects must be immutable +2010-11 https://mail.python.org/archives/list/python-ideas@python.org/thread/I325GHF3HGVHTP4EOYNXFCU2I7QR7JMZ + need complete isolation +2011-08 https://mail.python.org/archives/list/python-dev@python.org/thread/ICJ46G7EAXTXRCTWLDERJ4N7NCZOS4ML/#XSVBXPL44ZSGFRRKXDPCTEQ75NDOR22L + allows GIL removal +2012-01 https://mail.python.org/archives/list/python-dev@python.org/thread/J3BPTMFFFJSJO52FFYMCWFNUJDSAYTKU + exposed ref leaks +2012-02 https://mail.python.org/archives/list/python-dev@python.org/thread/U26PM3JR2SIJFTNYWCSQ3NQA6EWBX722 + exposed missing incref +2012-06 https://mail.python.org/archives/list/python-dev@python.org/thread/NQJ2EIIG5SO763VHK7AA55X4CCJCA3T6/#OX3CCLABXIUKLP3LYT4YC2YLZS4F5HYG + can't share signatures on shared builtins +2013-06 https://mail.python.org/archives/list/python-dev@python.org/thread/7OC242PC4QB6XDDUZFP3LKZRCZJPH2DK + exposed bug +2014-07 https://mail.python.org/archives/list/python-dev@python.org/thread/QZBJBAR726XVSQOHF7WFFPYJ4BVUDVRM/#5U43ZX5WTTACYDL427J3YQTW7G6B4CZQ + benefits of moving to heap types for stdlib modules + +2015-06 https://mail.python.org/archives/list/python-ideas@python.org/thread/SVEG3TDLKFVPYD4PMYLT6J5L4H5BDT7Z + my original post +2015-07 https://mail.python.org/archives/list/python-ideas@python.org/thread/UVNNFEXOY3RHQTE2653VCIWOW7TPPLMP + concurrency models +2015-09 https://mail.python.org/archives/list/python-dev@python.org/thread/WMHLJ2XUDWZC22CDWRIMQRYA5RSEIJFT + are subinterpreters really solving multi-core? +2017-05 https://mail.python.org/archives/list/python-ideas@python.org/thread/TYLXUOANY6LWSUVCQPGJKNPPHOUNC54R + my new post +2017-09 https://mail.python.org/archives/list/python-ideas@python.org/thread/HQQWEE527HG3ILJVKQTXVSJIQO6NUSIA +2017-09 https://mail.python.org/archives/list/python-dev@python.org/thread/NBWMA6LVD22XOUYC5ZMPBFWDQOECRP77 +2017-09 https://mail.python.org/archives/list/python-dev@python.org/thread/EG4FSFG5E3O22FTIUQOXMQ6X6B5X3DP7 +2017-12 https://mail.python.org/archives/list/python-dev@python.org/thread/BCSRGAMCYB3NGXNU42U66J56XNZVMQP2 + PEP 554 +2018-04 https://mail.python.org/archives/list/python-dev@python.org/thread/MDBM27UNMEFNTS4FQRS3QAPZJRGX2OP2 + PEP 573 +2018-07 https://mail.python.org/archives/list/python-ideas@python.org/thread/OX5FMIATOMPNRSAF54QH25SEEGZFFJWV + questions about subinterpreters +2019-01 https://mail.python.org/archives/list/python-dev@python.org/thread/JJ7UB5BNVBZ5NLNDBNNUGSQVR6CUBAK5 + numpy breaks in subinterpreters +2018-05 https://mail.python.org/archives/list/python-dev@python.org/thread/UVP753UFBAYMEVOKT24KMHVITFYWGNPV + my PyCon talk +2018-09 https://mail.python.org/archives/list/python-dev@python.org/thread/GVQOMWXUDYLBXZ2MMIDX5D6X7X42VQV7 + static globals +2019-11 https://mail.python.org/archives/list/python-dev@python.org/thread/PQBGECVGVYFTVDLBYURLCXA3T7IPEHHO + passing around tstate +2020-04 https://mail.python.org/archives/list/python-dev@python.org/thread/3HVRFWHDMWPNR367GXBILZ4JJAUQ2STZ + (me) delay PEP 554? +2020-04 https://mail.python.org/archives/list/python-dev@python.org/thread/3KS3KACCJBUCHUGRBZ3R6WUGZXOKKWZ5 + PEP 554 feedback +2020-04 https://mail.python.org/archives/list/python-dev@python.org/thread/S674C2BJ7NHKB3SOJF4VFRXVNQDNSCHP + get rid of static types? +2020-05 https://mail.python.org/archives/list/python-dev@python.org/thread/X2KPCSRVBD2QD5GP5IMXXZTGZ46OXD3D + PEP 554 +2020-05 https://mail.python.org/archives/list/python-dev@python.org/thread/S5GZZCEREZLA2PEMTVFBCDM52H4JSENR + Victor's experiment +2020-06 https://mail.python.org/archives/list/python-dev@python.org/thread/5YNWDIYECDQDYQ7IFYJS6K5HUDUAWTT6 + too many changes? +2020-06 https://mail.python.org/archives/list/python-dev@python.org/thread/EV7F7Z6PLPWJU7SD2UPFEYKYUWU4ZJXZ + PEP 620 +2020-07 https://mail.python.org/archives/list/python-dev@python.org/thread/EJF67ZM2HMLWCVKAYNU4JCATO7CRILOS + GIL in stable ABI? +2021-01 https://mail.python.org/archives/list/python-dev@python.org/thread/C4ILXGPKBJQYUN5YDMTJOEOX7RHOD4S3 + exposed refleaks +2021-03 https://mail.python.org/archives/list/capi-sig@python.org/thread/INLCGPMTYFLRTWQL7RB4MUQZ37JAFRAU + (me) no subinterpreters in limited API? +2021-03 https://mail.python.org/archives/list/capi-sig@python.org/thread/G7FLMXII4V2J4Q625PDJIZWZ6JQKSJRH + (me) get rid of stable API? +2021-12 https://mail.python.org/archives/list/python-dev@python.org/thread/PNLBJBNIQDMG2YYGPBCTGOKOAVXRBJWY + my plans for subinterpreters (need PEP?) +2021-12 https://mail.python.org/archives/list/python-dev@python.org/thread/X3ZOSP2A4RTSKTBZ4XYHROSJBONCEDID + (me) impact on big projects +2021-12 https://mail.python.org/archives/list/python-dev@python.org/thread/7O3FUA52QGTVDC6MDAV5WXKNFEDRK5D6 + (me) immortal objects +2012-12 https://mail.python.org/archives/list/python-dev@python.org/thread/QTY25AHCLOXRCQ2LADUUZFVKNVLLYS25 + static types and singletons in C-API + + Copyright ========= From da684f8becfdc77d35d627c32c587687f2182c76 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 28 Dec 2021 17:15:19 -0700 Subject: [PATCH 04/10] Flesh out the proposal a bit. --- pep-0679.rst | 190 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 151 insertions(+), 39 deletions(-) diff --git a/pep-0679.rst b/pep-0679.rst index 4946ec756de..60eaff876b1 100644 --- a/pep-0679.rst +++ b/pep-0679.rst @@ -21,20 +21,70 @@ subinterpreters and the merits of exposing them to Python code. However, that PEP purposefully avoids discussion about isolation, especially related to the GIL. This PEP fills that role. -The more isolation there is between interpreters, the more value they -can offer. Currently subinterpreters operate in -`relative isolation from one another `_. If they -were fully isolated then they could operate in parallel on multi-core -hosts. +Currently subinterpreters operate in +`relative isolation from one another `_. +However, there is a substantial amount of CPython's runtime state that +is shared. Improving isolation by mostly eliminating that shared state +provides a number of benefits. Most notably, they could fully operate +in parallel on multi-core hosts, where currently the GIL prevents this. -This proposal identifies a path forward to reach full isolation between -interpreters. This includes making the GIL per-interpreter. +This proposal identifies a path forward to reach maximum practical +isolation between interpreters. Notably, this includes the GIL. Proposal ======== -TBD +The specific objective here is to make the GIL per-interpreter. It +protects concurrent access of a lot of CPython's runtime state. So all +that state must move to each interpreter before the GIL can. + +CPython's runtime state is currently stored in the following places: + +* static/global C variables + + immutable, often const + + treated as immutable + + set during runtime init, then treated as immutable + + mutable, protected by the GIL + + mutable, protected by some other lock + + mutable, atomic +* ``_PyRuntime`` (``_PyRuntimeState``) +* ``PyInterpreterState``, held by ``_PyRuntime`` +* ``PyThreadState``, help by ``PyInterpreterState`` +* thread-specific storage (TSS) +* process-global resources that must be shared + +To get a per-interpreter GIL, the mutable globals protected by the GIL +must move to ``PyInterpreterState``, along with any similar parts of +``_PyRuntimeState``. This includes all Python objects, with some +possible exceptions. All other state can remain global. + +The Process +----------- + +At a high level, the process divides into two steps. First we +consolidate the GIL-protected state into ``_PyRuntimeState``. Then we +move it down into ``PyInterpreterState``. Some of these moves can be +done independently. Some must be done in a particular order. Some +must be done all at once. The final piece of global state to +become per-interpreter will be the GIL. + +The number of global variables to be moved is large, but most are +Python objects that can be dealt with in large groups (like +``Py_IDENTIFIER``). Dealing with all these globals is highly +mechanical. It doesn't require cleverness but instead requires +someone to put in the time. + +Additional Concerns +------------------- + +This PEP also addresses several less trivial issues: + +* global objects directly exposed in the C-API +* impact on extension module maintainers +* memory allocators +* preventing new global variables +* ... Motivation @@ -42,14 +92,72 @@ Motivation [Clearly explain why the existing language specification is inadequate to address the problem that the PEP solves.] +The fundamental problem we're solving here is a lack of true multi-core +parallelism in CPython's interpreter. The GIL is the cause. While it +usually isn't a problem in practice, at the very least it make Python's +multi-core story murky. + +Isolated interpreters are also an effective mechanism to support +certain concurrency models. PEP 554 discusses this in more detail. + +Benefits +-------------------- + +Most of the effort needed for a per-interpreter GIL has benefits that +make it worth doing anyway: + +* ... + + +Furthermore, much of the work here benefits other CPython-related +projects: + +* performance improvements ("faster-cpython") +* pre-fork application deployment +* extension module isolation (see PEP 384, PEP 489, PEP 630, etc.) +* embedding (e.g. improved runtime finalization) + Rationale ========= [Describe why particular design decisions were made.] + +no-gil... + + +Impact +====== + +Backwards Compatibility +----------------------- + +[Describe potential impact and severity on pre-existing code.] + +Alternate Python Implementations +-------------------------------- + +(not affected? this is CPython-only) + +Security Implications +--------------------- + +[How could a malicious user take advantage of this new feature?] + +Maintainability +--------------- + +... + +Performance +----------- + +... + + Concerns --------- +======== TBD @@ -59,6 +167,40 @@ Specification [Describe the syntax and semantics of any new language feature.] +State To Be Moved +----------------- + +catalog variables + +* ... + +Tooling +------- + +... + +Completed Work +-------------- + +At the time this PEP was written, the following work had already been +completed: + +* cleanup of runtime initialization (see PEP 432 / PEP 587) +* isolation for stdlib extension modules (see PEP 384 / PEP 3121 / PEP 489) +* addition of ``_PyRuntimeState`` +* ... + +Documentation +------------- + +TBD + + +How to Teach This +================= + +[How to teach users, new and experienced, how to apply the PEP to their work.] + About Subinterpreters ===================== @@ -189,36 +331,6 @@ stable. On the other hand, there isn't much of a sample size from which to judge the utility of the feature. -Backwards Compatibility -======================= - -[Describe potential impact and severity on pre-existing code.] - - -Alternate Python Implementations -================================ - -(not affected? this is CPython-only) - - -Security Implications -===================== - -[How could a malicious user take advantage of this new feature?] - - -How to Teach This -================= - -[How to teach users, new and experienced, how to apply the PEP to their work.] - - -Documentation -============= - -TBD - - Deferred Functionality ====================== From 8ed044e2a4d4fb361356d976e276e90b18526cee Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 4 Jan 2022 14:17:30 -0700 Subject: [PATCH 05/10] Update the "Proposal" section. --- pep-0679.rst | 71 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/pep-0679.rst b/pep-0679.rst index 60eaff876b1..ea5c45db289 100644 --- a/pep-0679.rst +++ b/pep-0679.rst @@ -16,17 +16,18 @@ Abstract CPython has supported multiple interpreters in the same process (AKA "subinterpreters") since version 1.5 (1997). The feature has been -available via the C-API. [c-api]_ PEP 554 discusses some of the value of -subinterpreters and the merits of exposing them to Python code. +available via the C-API. [c-api]_ :pep:`554` discusses some of the value +of subinterpreters and the merits of exposing them to Python code. However, that PEP purposefully avoids discussion about isolation, -especially related to the GIL. This PEP fills that role. +especially related to the GIL. This PEP fills that gap. Currently subinterpreters operate in `relative isolation from one another `_. However, there is a substantial amount of CPython's runtime state that is shared. Improving isolation by mostly eliminating that shared state -provides a number of benefits. Most notably, they could fully operate -in parallel on multi-core hosts, where currently the GIL prevents this. +provides a number of benefits. Most notably, interpreters could +fully operate in parallel on multi-core hosts, +where currently the GIL prevents this. This proposal identifies a path forward to reach maximum practical isolation between interpreters. Notably, this includes the GIL. @@ -35,14 +36,31 @@ isolation between interpreters. Notably, this includes the GIL. Proposal ======== -The specific objective here is to make the GIL per-interpreter. It -protects concurrent access of a lot of CPython's runtime state. So all -that state must move to each interpreter before the GIL can. +The specific objective here is to make the GIL per-interpreter. +At a high level, we'll accomplish that with the following steps: + +1. consolidate global state (including objects) into ``_PyRuntimeState`` +2. move it all down into ``PyInterpreterState`` +3. finally, move the GIL down into ``PyInterpreterState`` + +Some of the moves in (2) can be done independently, in parallel. +Some must be done in a particular order. +Some must be done all at once. + +Per-Interpreter State +--------------------- + +The GIL protects concurrent access of most of CPython's runtime state. +So all that GIL-protected global state must move to each interpreter +before the GIL can. + +(In some cases, other mechanisms can be used to ensure thread-safe +sharing instead, such as locks or "immortal" objects.) CPython's runtime state is currently stored in the following places: * static/global C variables - + immutable, often const + + immutable, often ``const`` + treated as immutable + set during runtime init, then treated as immutable + mutable, protected by the GIL @@ -50,33 +68,28 @@ CPython's runtime state is currently stored in the following places: + mutable, atomic * ``_PyRuntime`` (``_PyRuntimeState``) * ``PyInterpreterState``, held by ``_PyRuntime`` -* ``PyThreadState``, help by ``PyInterpreterState`` +* ``PyThreadState``, held by ``PyInterpreterState`` * thread-specific storage (TSS) +* passed through the C stack (starting in ``main()``) * process-global resources that must be shared To get a per-interpreter GIL, the mutable globals protected by the GIL must move to ``PyInterpreterState``, along with any similar parts of -``_PyRuntimeState``. This includes all Python objects, with some -possible exceptions. All other state can remain global. +``_PyRuntimeState``. This basically includes all Python objects. +All other state can remain global. -The Process ------------ +Scale of Work +------------- + +The number of global variables to be moved is large, but most +are Python objects that can be dealt with in large groups (like +``Py_IDENTIFIER``). In nearly all cases, dealing with all these +globals is highly mechanical. That doesn't require cleverness +but instead requires someone to put in the time. -At a high level, the process divides into two steps. First we -consolidate the GIL-protected state into ``_PyRuntimeState``. Then we -move it down into ``PyInterpreterState``. Some of these moves can be -done independently. Some must be done in a particular order. Some -must be done all at once. The final piece of global state to -become per-interpreter will be the GIL. - -The number of global variables to be moved is large, but most are -Python objects that can be dealt with in large groups (like -``Py_IDENTIFIER``). Dealing with all these globals is highly -mechanical. It doesn't require cleverness but instead requires -someone to put in the time. - -Additional Concerns -------------------- +(Note that at the time this PEP was written, many globals had +already been moved into ``_PyRuntimeState`` +or into ``PyInterpreterState`` or the equivalent.) This PEP also addresses several less trivial issues: From 51b2502794cbd652c72053f504c1f629decaa187 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 4 Jan 2022 15:31:48 -0700 Subject: [PATCH 06/10] Fill out the "Motivation" Section. --- pep-0679.rst | 53 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/pep-0679.rst b/pep-0679.rst index ea5c45db289..82f21d40710 100644 --- a/pep-0679.rst +++ b/pep-0679.rst @@ -93,42 +93,65 @@ or into ``PyInterpreterState`` or the equivalent.) This PEP also addresses several less trivial issues: -* global objects directly exposed in the C-API * impact on extension module maintainers +* global objects directly exposed in the C-API * memory allocators -* preventing new global variables -* ... +* how to prevent new global variables? Motivation ========== -[Clearly explain why the existing language specification is inadequate to address the problem that the PEP solves.] - The fundamental problem we're solving here is a lack of true multi-core -parallelism in CPython's interpreter. The GIL is the cause. While it -usually isn't a problem in practice, at the very least it make Python's -multi-core story murky. +parallelism (for Python code) in CPython's interpreter. The GIL is the +cause. While it usually isn't a problem in practice, at the very least +it make Python's multi-core story murky, which makes the GIL +a consistent distraction. Isolated interpreters are also an effective mechanism to support -certain concurrency models. PEP 554 discusses this in more detail. +certain concurrency models. :pep:`554` discusses this in more detail. -Benefits --------------------- +Other Benefits +-------------- Most of the effort needed for a per-interpreter GIL has benefits that make it worth doing anyway: -* ... - +* greatly reduces the number of C globals (best practice for C code) +* makes subinterpreter behavior more reliable +* fixes long-standing runtime bugs that otherwise haven't been prioritized +* exposes (and fix) previously unknown runtime bugs +* cleaner runtime initialization (:pep:`432`, :pep:`587`) +* cleaner and more complete runtime finalization +* makes it easier to identify runtime state (in one place rather than many files) +* makes it easier to statically allocate runtime state in a consistent way +* makes objects created via C and Python objects more consistent +* better memory locality for runtime state +* structural layering of the C-API (header files) +* more... Furthermore, much of the work here benefits other CPython-related projects: * performance improvements ("faster-cpython") * pre-fork application deployment -* extension module isolation (see PEP 384, PEP 489, PEP 630, etc.) -* embedding (e.g. improved runtime finalization) +* extension module isolation (see :pep:`384`, :pep:`489`, :pep:`630`, etc.) +* embedding + +Existing Use of Subinterpreters +------------------------------- + +Subinterpreters have been used (via the C-API) for many years. However, +until recently the feature wasn't widely known, not extensively used. +One notable public project has been using them a long time: mod_wsgi. +In the last few years they have been gaining traction. Here are some +of the public projects using subinterpreters currently: + +* mod_wsgi +* ... + +Note that, with :pep:`554`, subinterpreters usage would likely grow +significantly (via Python code rather than the C-API). Rationale From 0a18ddbde325c22f3de076b7a91b647289417923 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 4 Jan 2022 15:32:56 -0700 Subject: [PATCH 07/10] Start the "Rationale" section. --- pep-0679.rst | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/pep-0679.rst b/pep-0679.rst index 82f21d40710..49c67262f52 100644 --- a/pep-0679.rst +++ b/pep-0679.rst @@ -159,8 +159,67 @@ Rationale [Describe why particular design decisions were made.] +During initial investigations in 2014, a variety of possible solutions +for multi-core Python were explored, including: + +* release the GIL in extension modules +* other Python implementations (e.g. Jython, IronPython) +* remove the GIL (e.g. gilectomy, "no-gil") +* ``multiprocessing`` +* other parallelism tools (e.g. dask, ray, MPI) +* give up on multi-core (e.g. async, do nothing) + +Each had drawbacks without simple solutions: + +* extensions: doesn't help with Python code +* alt. implementations: CPython dominates +* get rid of the GIL: too much technical risk (at the time) +* multiprocessing: too much work to make it more accessible; high penalties in some situations (at large scale, Windows) +* new tools: not a fit for the stdlib +* give up: this can only end in tears + +Even in 2014 it was fairly clear that a solution using subinterpreters +did not have a high level of technical risk and that most of the work +was worth doing anyway. +(The downside was the volume of work to be done.) + +The "no-gil" Project +-------------------- + +Note that the "no-gil" project is currently active and may be successful +in removing the GIL. There isn't any real conflict with this PEP +and it is unlikely that one would prevent the other from succeeding. +Furthermore, they face a number of similar challenges. In fact +there is a fair amount of overlap in necessary work, which +would benefit both projects. + +At worst, one project might seem to make the other unnecessary. +However, they both have distinct value. Each supports a different +concurrency model to take advantage of multi-core. "no-gil" makes +the existing "threading" module support multi-core. This PEP does +so with an isolated "process" model (see :pep:`554`) using the +existing "subinterpreters" feature. Also, both the "threading" +module and subinterpreters are already used by enough people +that we couldn't remove them anyway. + +Objects in the C-API +-------------------- + +One non-trivial problem to be solved was what to do about Python objects +exposed in the public C-API (and stable ABI). There were only a few +valid options: + +* turn the symbols into lookup function calls +* only use the symbols as markers +* "immortal" objects -no-gil... +... + +Other Design Decisions +---------------------- + +* per-interpreter allocators +* ... Impact From 39bb7637cf5942a96385a41b802806ec9ce3b4f0 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 4 Jan 2022 15:35:34 -0700 Subject: [PATCH 08/10] Add TODO comments. --- pep-0679.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pep-0679.rst b/pep-0679.rst index 49c67262f52..fdbfd19a19a 100644 --- a/pep-0679.rst +++ b/pep-0679.rst @@ -147,6 +147,7 @@ One notable public project has been using them a long time: mod_wsgi. In the last few years they have been gaining traction. Here are some of the public projects using subinterpreters currently: +.. XXX * mod_wsgi * ... @@ -213,13 +214,17 @@ valid options: * only use the symbols as markers * "immortal" objects +.. XXX + ... Other Design Decisions ---------------------- +.. XXX + * per-interpreter allocators -* ... +* preventing new global variables Impact From 37557973d07005775403395287e727a5a6a377cb Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 4 Jan 2022 15:40:26 -0700 Subject: [PATCH 09/10] Add references to later sections. --- pep-0679.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pep-0679.rst b/pep-0679.rst index fdbfd19a19a..4875909bcf8 100644 --- a/pep-0679.rst +++ b/pep-0679.rst @@ -73,6 +73,8 @@ CPython's runtime state is currently stored in the following places: * passed through the C stack (starting in ``main()``) * process-global resources that must be shared +(See `State To Be Moved`_ for a detailed list.) + To get a per-interpreter GIL, the mutable globals protected by the GIL must move to ``PyInterpreterState``, along with any similar parts of ``_PyRuntimeState``. This basically includes all Python objects. @@ -96,7 +98,7 @@ This PEP also addresses several less trivial issues: * impact on extension module maintainers * global objects directly exposed in the C-API * memory allocators -* how to prevent new global variables? +* how to prevent new global variables? (See `Tooling`_.) Motivation From b2938a9e8a962101d1e73a4a3063a9bbf2eded7b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 4 Jan 2022 15:47:56 -0700 Subject: [PATCH 10/10] Minor tweaks. --- pep-0679.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pep-0679.rst b/pep-0679.rst index 4875909bcf8..87fe85202e1 100644 --- a/pep-0679.rst +++ b/pep-0679.rst @@ -146,8 +146,9 @@ Existing Use of Subinterpreters Subinterpreters have been used (via the C-API) for many years. However, until recently the feature wasn't widely known, not extensively used. One notable public project has been using them a long time: mod_wsgi. -In the last few years they have been gaining traction. Here are some -of the public projects using subinterpreters currently: +In the last few years they have been gaining traction. + +Here are some of the public projects using subinterpreters currently: .. XXX * mod_wsgi @@ -287,8 +288,8 @@ Completed Work At the time this PEP was written, the following work had already been completed: -* cleanup of runtime initialization (see PEP 432 / PEP 587) -* isolation for stdlib extension modules (see PEP 384 / PEP 3121 / PEP 489) +* cleanup of runtime initialization (see :pep:`432` / :pep:`587`) +* isolation for stdlib extension modules (see :pep:`384` / :pep:`3121` / :pep:`489`) * addition of ``_PyRuntimeState`` * ...