| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- #if !defined(TORCH_STABLE_ONLY) && !defined(TORCH_TARGET_VERSION)
- /*
- 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)
- PYBIND11_NAMESPACE_BEGIN(detail)
- inline PyInterpreterState *get_interpreter_state_unchecked() {
- auto cur_tstate = get_thread_state_unchecked();
- if (cur_tstate)
- return cur_tstate->interp;
- else
- return nullptr;
- }
- PYBIND11_NAMESPACE_END(detail)
- class subinterpreter;
- /// 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.
- class subinterpreter_scoped_activate {
- public:
- explicit subinterpreter_scoped_activate(subinterpreter const &si);
- ~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;
- };
- /// 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 inline 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)) {
- 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::get_num_interpreters_seen() += 1; // there are now many interpreters
- 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 inline 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;
- PyThreadState *old_tstate;
- // 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 && 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_);
- else
- 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;
- PyInterpreterState *istate_ = nullptr;
- PyThreadState *creation_tstate_ = 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_;
- };
- 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
- 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();
- PyThreadState_Clear(tstate_);
- PyThreadState_DeleteCurrent();
- }
- // Go back the previous interpreter (if any) and acquire THAT gil
- PyThreadState_Swap(old_tstate_);
- }
- }
- PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
- #else
- #error "This file should not be included when either TORCH_STABLE_ONLY or TORCH_TARGET_VERSION is defined."
- #endif // !defined(TORCH_STABLE_ONLY) && !defined(TORCH_TARGET_VERSION)
|