2020#endif
2121
2222PYBIND11_NAMESPACE_BEGIN (PYBIND11_NAMESPACE)
23+ PYBIND11_NAMESPACE_BEGIN(detail)
24+ PyInterpreterState *get_interpreter_state_unchecked() {
25+ auto cur_tstate = get_thread_state_unchecked ();
26+ if (cur_tstate)
27+ return cur_tstate->interp ;
28+ else
29+ return nullptr ;
30+ }
31+ PYBIND11_NAMESPACE_END ()
2332
2433class subinterpreter;
2534
@@ -38,7 +47,7 @@ class subinterpreter_scoped_activate {
3847
3948private:
4049 PyThreadState *old_tstate_ = nullptr ;
41- PyThreadState *free_tstate_ = nullptr ;
50+ PyThreadState *tstate_ = nullptr ;
4251 PyGILState_STATE gil_state_;
4352 bool simple_gil_ = false ;
4453};
@@ -139,13 +148,13 @@ class subinterpreter {
139148 auto *&internals_ptr_ptr = detail::get_internals_pp<detail::internals>();
140149 auto *&local_internals_ptr_ptr = detail::get_internals_pp<detail::local_internals>();
141150 {
142- dict state_dict = detail::get_python_state_dict ();
151+ dict sd = state_dict ();
143152 internals_ptr_ptr
144153 = detail::get_internals_pp_from_capsule_in_state_dict<detail::internals>(
145- state_dict , PYBIND11_INTERNALS_ID);
154+ sd , PYBIND11_INTERNALS_ID);
146155 local_internals_ptr_ptr
147156 = detail::get_internals_pp_from_capsule_in_state_dict<detail::local_internals>(
148- state_dict , detail::get_local_internals_id ());
157+ sd , detail::get_local_internals_id ());
149158 }
150159
151160 // End it
@@ -167,10 +176,6 @@ class subinterpreter {
167176 }
168177 }
169178
170- // / abandon cleanup of this subinterpreter (leak it). this might be needed during
171- // / finalization...
172- void disarm () { tstate_ = nullptr ; }
173-
174179 // / Get a handle to the main interpreter that can be used with subinterpreter_scoped_activate
175180 // / Note that destructing the handle is a noop, the main interpreter can only be ended by
176181 // / py::finalize_interpreter()
@@ -181,6 +186,30 @@ class subinterpreter {
181186 return subinterpreter_scoped_activate (m);
182187 }
183188
189+ // / Get a non-owning wrapper of the currently active interpreter (if any)
190+ static subinterpreter current () {
191+ subinterpreter c;
192+ c.istate_ = detail::get_interpreter_state_unchecked ();
193+ c.disarm (); // make destruct a noop, we don't own this...
194+ return c;
195+ }
196+
197+ // / Get the numerical identifier for the sub-interpreter
198+ int64_t id () const { return PyInterpreterState_GetID (istate_); }
199+
200+ // / Get the interpreter's state dict. This interpreter's GIL must be held before calling!
201+ dict state_dict () { return reinterpret_borrow<dict>(PyInterpreterState_GetDict (istate_)); }
202+
203+ // / abandon cleanup of this subinterpreter (leak it). this might be needed during
204+ // / finalization...
205+ void disarm () { tstate_ = nullptr ; }
206+
207+ // / An empty wrapper cannot be activated
208+ bool empty () const { return istate_ == nullptr ; }
209+
210+ // / Is this wrapper non-empty
211+ explicit operator bool () const { return !empty (); }
212+
184213private:
185214 friend class subinterpreter_scoped_activate ;
186215 PyThreadState *tstate_ = nullptr ;
@@ -200,14 +229,11 @@ class scoped_subinterpreter {
200229};
201230
202231inline subinterpreter_scoped_activate::subinterpreter_scoped_activate (subinterpreter const &si) {
203- #if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
204232 if (!si.istate_ ) {
205233 pybind11_fail (" null subinterpreter" );
206234 }
207- #endif
208235
209- auto cur_tstate = detail::get_thread_state_unchecked ();
210- if (cur_tstate && cur_tstate->interp == si.istate_ ) {
236+ if (detail::get_interpreter_state_unchecked () == si.istate_ ) {
211237 // we are already on this interpreter, make sure we hold the GIL
212238 simple_gil_ = true ;
213239 gil_state_ = PyGILState_Ensure ();
@@ -216,28 +242,28 @@ inline subinterpreter_scoped_activate::subinterpreter_scoped_activate(subinterpr
216242
217243 // we can't really innteract with the interpreter at all until we switch to it
218244 // not even to, for example, look in it's state dict or touch its internals
219- free_tstate_ = PyThreadState_New (si.istate_ );
245+ tstate_ = PyThreadState_New (si.istate_ );
220246
221247 // make the interpreter active and acquire the GIL
222- old_tstate_ = PyThreadState_Swap (free_tstate_ );
248+ old_tstate_ = PyThreadState_Swap (tstate_ );
223249
224250 // save this in internals for scoped_gil calls
225- PYBIND11_TLS_REPLACE_VALUE (detail::get_internals ().tstate , free_tstate_ );
251+ PYBIND11_TLS_REPLACE_VALUE (detail::get_internals ().tstate , tstate_ );
226252}
227253
228254inline subinterpreter_scoped_activate::~subinterpreter_scoped_activate () {
229255 if (simple_gil_) {
230256 // We were on this interpreter already, so just make sure the GIL goes back as it was
231257 PyGILState_Release (gil_state_);
232258 } else {
233- if (free_tstate_ ) {
259+ if (tstate_ ) {
234260#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
235- if (detail::get_thread_state_unchecked () != free_tstate_ ) {
261+ if (detail::get_thread_state_unchecked () != tstate_ ) {
236262 pybind11_fail (" ~subinterpreter_scoped_activate: thread state must be current!" );
237263 }
238264#endif
239265 PYBIND11_TLS_DELETE_VALUE (detail::get_internals ().tstate );
240- PyThreadState_Clear (free_tstate_ );
266+ PyThreadState_Clear (tstate_ );
241267 PyThreadState_DeleteCurrent ();
242268 }
243269
0 commit comments