| /* |
| pybind11/subinterpreter.h: Support for creating and using subinterpreters |
| |
| Copyright (c) 2025 The Pybind Development Team. |
| |
| All rights reserved. Use of this source code is governed by a |
| BSD-style license that can be found in the LICENSE file. |
| */ |
| |
| #pragma once |
| |
| #include "detail/common.h" |
| #include "detail/internals.h" |
| #include "gil.h" |
| |
| #include <stdexcept> |
| |
| #ifndef PYBIND11_HAS_SUBINTERPRETER_SUPPORT |
| # error "This platform does not support subinterpreters, do not include this file." |
| #endif |
| |
| PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) |
| |
| class subinterpreter; |
| class subinterpreter_thread_state; |
| |
| /// Activate the subinterpreter and acquire its GIL, while also releasing any GIL and interpreter |
| /// currently held. Upon exiting the scope, the previous subinterpreter (if any) and its |
| /// associated GIL are restored to their state as they were before the scope was entered. |
| /// |
| /// Two construction modes are supported: |
| /// |
| /// 1. `subinterpreter_scoped_activate(subinterpreter const &)`: |
| /// Transient mode (the default). A fresh PyThreadState is created on entry and destroyed on |
| /// exit. This is the established behavior; existing code is unaffected. |
| /// |
| /// 2. `subinterpreter_scoped_activate(subinterpreter_thread_state &)`: |
| /// Reuse mode. The PyThreadState owned by the given subinterpreter_thread_state is swapped |
| /// in on entry and swapped out (but NOT destroyed) on exit, so repeated activations on the |
| /// same OS thread reuse the same PyThreadState and preserve its per-thread interpreter state. |
| /// Use this when a single OS thread re-enters one or more subinterpreters many times. |
| class subinterpreter_scoped_activate { |
| public: |
| explicit subinterpreter_scoped_activate(subinterpreter const &si); |
| explicit subinterpreter_scoped_activate(subinterpreter_thread_state &ts); |
| ~subinterpreter_scoped_activate(); |
| |
| subinterpreter_scoped_activate(subinterpreter_scoped_activate &&) = delete; |
| subinterpreter_scoped_activate(subinterpreter_scoped_activate const &) = delete; |
| subinterpreter_scoped_activate &operator=(subinterpreter_scoped_activate &) = delete; |
| subinterpreter_scoped_activate &operator=(subinterpreter_scoped_activate const &) = delete; |
| |
| private: |
| PyThreadState *old_tstate_ = nullptr; |
| PyThreadState *tstate_ = nullptr; |
| PyGILState_STATE gil_state_; |
| bool simple_gil_ = false; |
| // When true, tstate_ is owned by a subinterpreter_thread_state and must NOT be destroyed |
| // when this scope exits (only swapped out). |
| bool borrowed_ = false; |
| }; |
| |
| /// Holds a Python subinterpreter instance |
| class subinterpreter { |
| public: |
| /// empty/unusable, but move-assignable. use create() to create a subinterpreter. |
| subinterpreter() = default; |
| |
| subinterpreter(subinterpreter const ©) = delete; |
| subinterpreter &operator=(subinterpreter const ©) = delete; |
| |
| subinterpreter(subinterpreter &&old) noexcept |
| : istate_(old.istate_), creation_tstate_(old.creation_tstate_) { |
| old.istate_ = nullptr; |
| old.creation_tstate_ = nullptr; |
| } |
| |
| subinterpreter &operator=(subinterpreter &&old) noexcept { |
| std::swap(old.istate_, istate_); |
| std::swap(old.creation_tstate_, creation_tstate_); |
| return *this; |
| } |
| |
| /// Create a new subinterpreter with the specified configuration |
| /// @note This function acquires (and then releases) the main interpreter GIL, but the main |
| /// interpreter and its GIL are not required to be held prior to calling this function. |
| static subinterpreter create(PyInterpreterConfig const &cfg) { |
| |
| error_scope err_scope; |
| subinterpreter result; |
| { |
| // we must hold the main GIL in order to create a subinterpreter |
| subinterpreter_scoped_activate main_guard(main()); |
| |
| auto *prev_tstate = PyThreadState_Get(); |
| |
| PyStatus status; |
| |
| { |
| /* |
| Several internal CPython modules are lacking proper subinterpreter support in 3.12 |
| even though it is "stable" in that version. This most commonly seems to cause |
| crashes when two interpreters concurrently initialize, which imports several things |
| (like builtins, unicode, codecs). |
| */ |
| #if PY_VERSION_HEX < 0x030D0000 && defined(Py_MOD_PER_INTERPRETER_GIL_SUPPORTED) |
| static std::mutex one_at_a_time; |
| std::lock_guard<std::mutex> guard(one_at_a_time); |
| #endif |
| status = Py_NewInterpreterFromConfig(&result.creation_tstate_, &cfg); |
| } |
| |
| // this doesn't raise a normal Python exception, it provides an exit() status code. |
| if (PyStatus_Exception(status) != 0) { |
| pybind11_fail("failed to create new sub-interpreter"); |
| } |
| |
| // upon success, the new interpreter is activated in this thread |
| result.istate_ = result.creation_tstate_->interp; |
| detail::has_seen_non_main_interpreter() = true; |
| detail::get_internals(); // initialize internals.tstate, amongst other things... |
| |
| // In 3.13+ this state should be deleted right away, and the memory will be reused for |
| // the next threadstate on this interpreter. However, on 3.12 we cannot do that, we |
| // must keep it around (but not use it) ... see destructor. |
| #if PY_VERSION_HEX >= 0x030D0000 |
| PyThreadState_Clear(result.creation_tstate_); |
| PyThreadState_DeleteCurrent(); |
| #endif |
| |
| // we have to switch back to main, and then the scopes will handle cleanup |
| PyThreadState_Swap(prev_tstate); |
| } |
| return result; |
| } |
| |
| /// Calls create() with a default configuration of an isolated interpreter that disallows fork, |
| /// exec, and Python threads. |
| static subinterpreter create() { |
| // same as the default config in the python docs |
| PyInterpreterConfig cfg; |
| std::memset(&cfg, 0, sizeof(cfg)); |
| cfg.allow_threads = 1; |
| cfg.check_multi_interp_extensions = 1; |
| cfg.gil = PyInterpreterConfig_OWN_GIL; |
| return create(cfg); |
| } |
| |
| ~subinterpreter() { |
| if (!creation_tstate_) { |
| // non-owning wrapper, do nothing. |
| return; |
| } |
| |
| PyThreadState *destroy_tstate = nullptr; |
| PyThreadState *old_tstate = nullptr; |
| |
| // Python 3.12 requires us to keep the original PyThreadState alive until we are ready to |
| // destroy the interpreter. We prefer to use that to destroy the interpreter. |
| #if PY_VERSION_HEX < 0x030D0000 |
| // The tstate passed to Py_EndInterpreter MUST have been created on the current OS thread. |
| bool same_thread = false; |
| # ifdef PY_HAVE_THREAD_NATIVE_ID |
| same_thread = PyThread_get_thread_native_id() == creation_tstate_->native_thread_id; |
| # endif |
| if (same_thread) { |
| // OK it is safe to use the creation state here |
| destroy_tstate = creation_tstate_; |
| old_tstate = PyThreadState_Swap(destroy_tstate); |
| } else { |
| // We have to make a new tstate on this thread and use that. |
| destroy_tstate = PyThreadState_New(istate_); |
| old_tstate = PyThreadState_Swap(destroy_tstate); |
| |
| // We can use the one we just created, so we must delete the creation state. |
| PyThreadState_Clear(creation_tstate_); |
| PyThreadState_Delete(creation_tstate_); |
| } |
| #else |
| destroy_tstate = PyThreadState_New(istate_); |
| old_tstate = PyThreadState_Swap(destroy_tstate); |
| #endif |
| |
| bool switch_back = (old_tstate != nullptr) && old_tstate->interp != istate_; |
| |
| // Internals always exists in the subinterpreter, this class enforces it when it creates |
| // the subinterpreter. Even if it didn't, this only creates the pointer-to-pointer, not the |
| // internals themselves. |
| detail::get_internals_pp_manager().get_pp(); |
| detail::get_local_internals_pp_manager().get_pp(); |
| |
| // End it |
| Py_EndInterpreter(destroy_tstate); |
| |
| // It's possible for the internals to be created during endinterpreter (e.g. if a |
| // py::capsule calls `get_internals()` during destruction), so we destroy afterward. |
| detail::get_internals_pp_manager().destroy(); |
| detail::get_local_internals_pp_manager().destroy(); |
| |
| // switch back to the old tstate and old GIL (if there was one) |
| if (switch_back) { |
| PyThreadState_Swap(old_tstate); |
| } |
| } |
| |
| /// Get a handle to the main interpreter that can be used with subinterpreter_scoped_activate |
| /// Note that destructing the handle is a noop, the main interpreter can only be ended by |
| /// py::finalize_interpreter() |
| static subinterpreter main() { |
| subinterpreter m; |
| m.istate_ = PyInterpreterState_Main(); |
| m.disarm(); // make destruct a noop |
| return m; |
| } |
| |
| /// Get a non-owning wrapper of the currently active interpreter (if any) |
| static subinterpreter current() { |
| subinterpreter c; |
| c.istate_ = detail::get_interpreter_state_unchecked(); |
| c.disarm(); // make destruct a noop, we don't own this... |
| return c; |
| } |
| |
| /// Get the numerical identifier for the sub-interpreter |
| int64_t id() const { |
| if (istate_ != nullptr) { |
| return PyInterpreterState_GetID(istate_); |
| } |
| return -1; // CPython uses one-up numbers from 0, so negative should be safe to return |
| // here. |
| } |
| |
| /// Get the interpreter's state dict. This interpreter's GIL must be held before calling! |
| dict state_dict() { return reinterpret_borrow<dict>(PyInterpreterState_GetDict(istate_)); } |
| |
| /// abandon cleanup of this subinterpreter (leak it). this might be needed during |
| /// finalization... |
| void disarm() { creation_tstate_ = nullptr; } |
| |
| /// An empty wrapper cannot be activated |
| bool empty() const { return istate_ == nullptr; } |
| |
| /// Is this wrapper non-empty |
| explicit operator bool() const { return !empty(); } |
| |
| private: |
| friend class subinterpreter_scoped_activate; |
| friend class subinterpreter_thread_state; |
| PyInterpreterState *istate_ = nullptr; |
| PyThreadState *creation_tstate_ = nullptr; |
| }; |
| |
| /// RAII wrapper that owns a PyThreadState bound to a specific subinterpreter on the OS thread |
| /// that constructed it. Intended to be held long-lived (e.g. as a `thread_local`, or inside a |
| /// per-thread struct) so that many subinterpreter_scoped_activate scopes on the same OS thread |
| /// can reuse a single PyThreadState instead of creating and destroying one each time. |
| /// |
| /// The PyThreadState is created on construction in a *released* state: it is NOT made current, |
| /// and no GIL is acquired. Activation is the job of subinterpreter_scoped_activate. |
| /// |
| /// A single OS thread can hold one of these per subinterpreter and alternate between them via |
| /// subinterpreter_scoped_activate without churning PyThreadState objects. |
| /// |
| /// Lifetime / threading requirements: |
| /// |
| /// - Construction and destruction must happen on the SAME OS thread (a PyThreadState is bound |
| /// to the OS thread that created it; deleting it on a different thread is undefined behavior). |
| /// - The owning subinterpreter must still be alive when this object is destroyed. |
| /// - This object must NOT be destroyed while a subinterpreter_scoped_activate referring to it is |
| /// still alive (the activator holds a reference into it). |
| /// |
| /// Typical usage: |
| /// |
| /// @code |
| /// thread_local py::subinterpreter_thread_state ts(sub); |
| /// { |
| /// py::subinterpreter_scoped_activate guard(ts); // swap-in only |
| /// // ... use the subinterpreter ... |
| /// } // swap-out, tstate kept alive |
| /// { |
| /// py::subinterpreter_scoped_activate guard(ts); // reuses the same PyThreadState |
| /// // ... |
| /// } |
| /// @endcode |
| class subinterpreter_thread_state { |
| public: |
| /// Create a PyThreadState for `si` on the calling OS thread. The new state is left in a |
| /// released state (not current, no GIL acquired). |
| explicit subinterpreter_thread_state(subinterpreter const &si); |
| |
| /// Destroy the owned PyThreadState. Must run on the same OS thread that constructed this |
| /// object, while the owning subinterpreter is still alive, and while no |
| /// subinterpreter_scoped_activate referring to this object is alive. |
| ~subinterpreter_thread_state(); |
| |
| subinterpreter_thread_state(subinterpreter_thread_state const &) = delete; |
| subinterpreter_thread_state(subinterpreter_thread_state &&) = delete; |
| subinterpreter_thread_state &operator=(subinterpreter_thread_state const &) = delete; |
| subinterpreter_thread_state &operator=(subinterpreter_thread_state &&) = delete; |
| |
| /// The interpreter this thread state belongs to. |
| PyInterpreterState *interpreter_state() const { return istate_; } |
| |
| /// The owned PyThreadState pointer; valid for the lifetime of this object. |
| PyThreadState *raw_thread_state() const { return tstate_; } |
| |
| private: |
| friend class subinterpreter_scoped_activate; |
| PyThreadState *tstate_ = nullptr; |
| PyInterpreterState *istate_ = nullptr; |
| }; |
| |
| class scoped_subinterpreter { |
| public: |
| scoped_subinterpreter() : si_(subinterpreter::create()), scope_(si_) {} |
| |
| explicit scoped_subinterpreter(PyInterpreterConfig const &cfg) |
| : si_(subinterpreter::create(cfg)), scope_(si_) {} |
| |
| private: |
| subinterpreter si_; |
| subinterpreter_scoped_activate scope_; |
| }; |
| |
| // --- subinterpreter_scoped_activate ----------------------------------------------------------- |
| |
| inline subinterpreter_scoped_activate::subinterpreter_scoped_activate(subinterpreter const &si) { |
| if (!si.istate_) { |
| pybind11_fail("null subinterpreter"); |
| } |
| |
| if (detail::get_interpreter_state_unchecked() == si.istate_) { |
| // we are already on this interpreter, make sure we hold the GIL |
| simple_gil_ = true; |
| gil_state_ = PyGILState_Ensure(); |
| return; |
| } |
| |
| // we can't really interact with the interpreter at all until we switch to it |
| // not even to, for example, look in its state dict or touch its internals |
| tstate_ = PyThreadState_New(si.istate_); |
| |
| // make the interpreter active and acquire the GIL |
| old_tstate_ = PyThreadState_Swap(tstate_); |
| |
| // save this in internals for scoped_gil calls (see also: PR #5870) |
| detail::get_internals().tstate = tstate_; |
| } |
| |
| inline subinterpreter_scoped_activate::subinterpreter_scoped_activate( |
| subinterpreter_thread_state &ts) { |
| if (ts.tstate_ == nullptr) { |
| pybind11_fail("subinterpreter_scoped_activate: empty subinterpreter_thread_state"); |
| } |
| |
| if (detail::get_interpreter_state_unchecked() == ts.istate_) { |
| // We are already on this interpreter -- e.g. nested activation, or a different |
| // PyThreadState for the same interpreter is already current on this thread. Match the |
| // fast path of the (subinterpreter const&) overload: just ensure the GIL is held. The |
| // `ts` argument's PyThreadState is intentionally NOT swapped to here; the already-current |
| // tstate keeps being used until the outer scope exits. |
| simple_gil_ = true; |
| gil_state_ = PyGILState_Ensure(); |
| return; |
| } |
| |
| #if defined(PYBIND11_DETAILED_ERROR_MESSAGES) |
| { |
| // A PyThreadState is bound to its creating OS thread; it may only be activated there. |
| bool same_thread = true; |
| # ifdef PY_HAVE_THREAD_NATIVE_ID |
| same_thread = PyThread_get_thread_native_id() == ts.tstate_->native_thread_id; |
| # endif |
| if (!same_thread) { |
| pybind11_fail("subinterpreter_scoped_activate: a subinterpreter_thread_state must be " |
| "activated on the same OS thread that constructed it!"); |
| } |
| } |
| #endif |
| |
| tstate_ = ts.tstate_; |
| borrowed_ = true; |
| |
| // make the interpreter active and acquire the GIL |
| old_tstate_ = PyThreadState_Swap(tstate_); |
| |
| // save this in internals for scoped_gil calls (see also: PR #5870) |
| detail::get_internals().tstate = tstate_; |
| } |
| |
| inline subinterpreter_scoped_activate::~subinterpreter_scoped_activate() { |
| if (simple_gil_) { |
| // We were on this interpreter already, so just make sure the GIL goes back as it was |
| PyGILState_Release(gil_state_); |
| } else { |
| if (tstate_) { |
| #if defined(PYBIND11_DETAILED_ERROR_MESSAGES) |
| if (detail::get_thread_state_unchecked() != tstate_) { |
| pybind11_fail("~subinterpreter_scoped_activate: thread state must be current!"); |
| } |
| #endif |
| detail::get_internals().tstate.reset(); |
| if (!borrowed_) { |
| PyThreadState_Clear(tstate_); |
| PyThreadState_DeleteCurrent(); |
| } |
| // When borrowed_, tstate_ stays alive in its owning subinterpreter_thread_state for |
| // reuse; the PyThreadState_Swap below merely detaches it from this thread. |
| } |
| |
| // Go back the previous interpreter (if any) and acquire THAT gil |
| PyThreadState_Swap(old_tstate_); |
| } |
| } |
| |
| // --- subinterpreter_thread_state -------------------------------------------------------------- |
| |
| inline subinterpreter_thread_state::subinterpreter_thread_state(subinterpreter const &si) { |
| if (!si.istate_) { |
| pybind11_fail("subinterpreter_thread_state: null subinterpreter"); |
| } |
| istate_ = si.istate_; |
| // PyThreadState_New does not require holding any GIL and does not make the new state current. |
| tstate_ = PyThreadState_New(istate_); |
| if (tstate_ == nullptr) { |
| pybind11_fail("subinterpreter_thread_state: PyThreadState_New returned null"); |
| } |
| } |
| |
| inline subinterpreter_thread_state::~subinterpreter_thread_state() { |
| if (tstate_ == nullptr) { |
| return; |
| } |
| #if defined(PYBIND11_DETAILED_ERROR_MESSAGES) |
| { |
| // A PyThreadState must be cleared and deleted on the OS thread that created it. |
| bool same_thread = true; |
| # ifdef PY_HAVE_THREAD_NATIVE_ID |
| same_thread = PyThread_get_thread_native_id() == tstate_->native_thread_id; |
| # endif |
| if (!same_thread) { |
| pybind11_fail("~subinterpreter_thread_state: must be destroyed on the same OS thread " |
| "that constructed it!"); |
| } |
| } |
| #endif |
| // The PyThreadState must be made current to be cleared and deleted on the owning OS thread. |
| // Swap it in (which acquires the subinterpreter's GIL), clear+delete, then restore whatever |
| // was active before. |
| PyThreadState *prev = PyThreadState_Swap(tstate_); |
| PyThreadState_Clear(tstate_); |
| PyThreadState_DeleteCurrent(); |
| // If `prev` is tstate_ itself, the user destroyed this object while it was active via a |
| // subinterpreter_scoped_activate -- a contract violation, but be defensive: do NOT swap back |
| // to a now-deleted pointer. Leaving the thread with no current interpreter is consistent |
| // with the cached state having just been destroyed. |
| if (prev != nullptr && prev != tstate_) { |
| PyThreadState_Swap(prev); |
| } |
| } |
| |
| PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) |