Skip to content

Commit e903406

Browse files
b-passhenryiii
authored andcommitted
Add some convenience accessors
1 parent 7a00f32 commit e903406

File tree

1 file changed

+44
-18
lines changed

1 file changed

+44
-18
lines changed

include/pybind11/subinterpreter.h

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@
2020
#endif
2121

2222
PYBIND11_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

2433
class subinterpreter;
2534

@@ -38,7 +47,7 @@ class subinterpreter_scoped_activate {
3847

3948
private:
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+
184213
private:
185214
friend class subinterpreter_scoped_activate;
186215
PyThreadState *tstate_ = nullptr;
@@ -200,14 +229,11 @@ class scoped_subinterpreter {
200229
};
201230

202231
inline 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

228254
inline 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

Comments
 (0)