mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 06:35:12 +00:00
significant redesign of GIL state handling
This commit is contained in:
parent
18fb3e323a
commit
39e97e6a49
@ -55,6 +55,13 @@ PYBIND11_NOINLINE inline internals &get_internals() {
|
|||||||
internals_ptr = caps;
|
internals_ptr = caps;
|
||||||
} else {
|
} else {
|
||||||
internals_ptr = new internals();
|
internals_ptr = new internals();
|
||||||
|
#if defined(WITH_THREAD)
|
||||||
|
PyEval_InitThreads();
|
||||||
|
PyThreadState *tstate = PyThreadState_Get();
|
||||||
|
internals_ptr->tstate = PyThread_create_key();
|
||||||
|
PyThread_set_key_value(internals_ptr->tstate, tstate);
|
||||||
|
internals_ptr->istate = tstate->interp;
|
||||||
|
#endif
|
||||||
builtins[id] = capsule(internals_ptr);
|
builtins[id] = capsule(internals_ptr);
|
||||||
}
|
}
|
||||||
return *internals_ptr;
|
return *internals_ptr;
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include <frameobject.h>
|
#include <frameobject.h>
|
||||||
|
#include <pythread.h>
|
||||||
|
|
||||||
#ifdef isalnum
|
#ifdef isalnum
|
||||||
# undef isalnum
|
# undef isalnum
|
||||||
@ -127,6 +128,9 @@
|
|||||||
} \
|
} \
|
||||||
PyObject *pybind11_init()
|
PyObject *pybind11_init()
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern PyThreadState *_PyThreadState_Current;
|
||||||
|
};
|
||||||
|
|
||||||
NAMESPACE_BEGIN(pybind11)
|
NAMESPACE_BEGIN(pybind11)
|
||||||
|
|
||||||
@ -233,6 +237,10 @@ struct internals {
|
|||||||
std::unordered_map<const void *, void*> registered_types_py; // PyTypeObject* -> type_info
|
std::unordered_map<const void *, void*> registered_types_py; // PyTypeObject* -> type_info
|
||||||
std::unordered_map<const void *, void*> registered_instances; // void * -> PyObject*
|
std::unordered_map<const void *, void*> registered_instances; // void * -> PyObject*
|
||||||
std::unordered_set<std::pair<const PyObject *, const char *>, overload_hash> inactive_overload_cache;
|
std::unordered_set<std::pair<const PyObject *, const char *>, overload_hash> inactive_overload_cache;
|
||||||
|
#if defined(WITH_THREAD)
|
||||||
|
int tstate = 0;
|
||||||
|
PyInterpreterState *istate = nullptr;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Return a reference to the current 'internals' information
|
/// Return a reference to the current 'internals' information
|
||||||
|
@ -1039,21 +1039,106 @@ template <typename InputType, typename OutputType> void implicitly_convertible()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(WITH_THREAD)
|
#if defined(WITH_THREAD)
|
||||||
inline void init_threading() { PyEval_InitThreads(); }
|
|
||||||
|
/* The functions below essentially reproduce the PyGILState_* API using a RAII
|
||||||
|
* pattern, but there are a few important differences:
|
||||||
|
*
|
||||||
|
* 1. When acquiring the GIL from an non-main thread during the finalization
|
||||||
|
* phase, the GILState API blindly terminates the calling thread, which
|
||||||
|
* is often not what is wanted. This API does not do this.
|
||||||
|
*
|
||||||
|
* 2. The gil_scoped_release function can optionally cut the relationship
|
||||||
|
* of a PyThreadState and its associated thread, which allows moving it to
|
||||||
|
* another thread (this is a fairly rare/advanced use case).
|
||||||
|
*
|
||||||
|
* 3. The reference count of an acquired thread state can be controlled. This
|
||||||
|
* can be handy to prevent cases where callbacks issued from an external
|
||||||
|
* thread constantly construct and destroy thread state data structures. */
|
||||||
|
|
||||||
class gil_scoped_acquire {
|
class gil_scoped_acquire {
|
||||||
PyGILState_STATE state;
|
|
||||||
public:
|
public:
|
||||||
inline gil_scoped_acquire() { state = PyGILState_Ensure(); }
|
gil_scoped_acquire() {
|
||||||
inline ~gil_scoped_acquire() { PyGILState_Release(state); }
|
auto const &internals = detail::get_internals();
|
||||||
|
tstate = (PyThreadState *) PyThread_get_key_value(internals.tstate);
|
||||||
|
|
||||||
|
if (!tstate) {
|
||||||
|
tstate = PyThreadState_New(internals.istate);
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
if (!tstate)
|
||||||
|
pybind11_fail("scoped_acquire: could not create thread state!");
|
||||||
|
#endif
|
||||||
|
tstate->gilstate_counter = 0;
|
||||||
|
PyThread_set_key_value(internals.tstate, tstate);
|
||||||
|
} else {
|
||||||
|
release = _PyThreadState_Current != tstate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (release) {
|
||||||
|
PyInterpreterState *interp = tstate->interp;
|
||||||
|
/* Work around an annoying assertion in PyThreadState_Swap */
|
||||||
|
tstate->interp = nullptr;
|
||||||
|
PyEval_AcquireThread(tstate);
|
||||||
|
tstate->interp = interp;
|
||||||
|
}
|
||||||
|
|
||||||
|
inc_ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
void inc_ref() {
|
||||||
|
++tstate->gilstate_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dec_ref() {
|
||||||
|
--tstate->gilstate_counter;
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
if (_PyThreadState_Current != tstate)
|
||||||
|
pybind11_fail("scoped_acquire::dec_ref(): thread state must be current!");
|
||||||
|
if (tstate->gilstate_counter < 0)
|
||||||
|
pybind11_fail("scoped_acquire::dec_ref(): reference count underflow!");
|
||||||
|
#endif
|
||||||
|
if (tstate->gilstate_counter == 0) {
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
if (!release)
|
||||||
|
pybind11_fail("scoped_acquire::dec_ref(): internal error!");
|
||||||
|
#endif
|
||||||
|
PyThreadState_Clear(tstate);
|
||||||
|
PyThreadState_DeleteCurrent();
|
||||||
|
PyThread_set_key_value(detail::get_internals().tstate, nullptr);
|
||||||
|
release = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~gil_scoped_acquire() {
|
||||||
|
dec_ref();
|
||||||
|
if (release)
|
||||||
|
PyEval_SaveThread();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
PyThreadState *tstate = nullptr;
|
||||||
|
bool release = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
class gil_scoped_release {
|
class gil_scoped_release {
|
||||||
PyThreadState *state;
|
|
||||||
public:
|
public:
|
||||||
inline gil_scoped_release() { state = PyEval_SaveThread(); }
|
gil_scoped_release(bool disassoc = false) : disassoc(disassoc) {
|
||||||
inline ~gil_scoped_release() { PyEval_RestoreThread(state); }
|
tstate = PyEval_SaveThread();
|
||||||
|
if (disassoc)
|
||||||
|
PyThread_set_key_value(detail::get_internals().tstate, nullptr);
|
||||||
|
}
|
||||||
|
~gil_scoped_release() {
|
||||||
|
if (!tstate)
|
||||||
|
return;
|
||||||
|
PyEval_RestoreThread(tstate);
|
||||||
|
if (disassoc)
|
||||||
|
PyThread_set_key_value(detail::get_internals().tstate, tstate);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
PyThreadState *tstate;
|
||||||
|
bool disassoc;
|
||||||
};
|
};
|
||||||
|
#else
|
||||||
|
class gil_scoped_acquire { };
|
||||||
|
class gil_scoped_release { };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
inline function get_overload(const void *this_ptr, const char *name) {
|
inline function get_overload(const void *this_ptr, const char *name) {
|
||||||
|
Loading…
Reference in New Issue
Block a user