File tree Expand file tree Collapse file tree 3 files changed +39
-1
lines changed Expand file tree Collapse file tree 3 files changed +39
-1
lines changed Original file line number Diff line number Diff line change @@ -1661,9 +1661,13 @@ class gil_scoped_acquire {
16611661class gil_scoped_release {
16621662public:
16631663 explicit gil_scoped_release (bool disassoc = false ) : disassoc(disassoc) {
1664+ // `get_internals()` must be called here unconditionally in order to initialize
1665+ // `internals.tstate` for subsequent `gil_scoped_acquire` calls. Otherwise, an
1666+ // initialization race could occur as multiple threads try `gil_scoped_acquire`.
1667+ const auto &internals = detail::get_internals ();
16641668 tstate = PyEval_SaveThread ();
16651669 if (disassoc) {
1666- auto key = detail::get_internals () .tstate ;
1670+ auto key = internals .tstate ;
16671671 #if PY_MAJOR_VERSION < 3
16681672 PyThread_delete_key_value (key);
16691673 #else
Original file line number Diff line number Diff line change 2626 target_link_libraries (test_embed PRIVATE ${PYTHON_LIBRARIES} )
2727endif ()
2828
29+ find_package (Threads REQUIRED)
30+ target_link_libraries (test_embed PUBLIC ${CMAKE_THREAD_LIBS_INIT} )
31+
2932add_custom_target (cpptest COMMAND $<TARGET_FILE:test_embed>
3033 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )
3134add_dependencies (check cpptest)
Original file line number Diff line number Diff line change 11#include < pybind11/embed.h>
22#include < catch.hpp>
33
4+ #include < thread>
5+
46namespace py = pybind11;
57using namespace py ::literals;
68
@@ -185,3 +187,32 @@ TEST_CASE("Execution frame") {
185187 py::exec (" var = dict(number=42)" );
186188 REQUIRE (py::globals ()[" var" ][" number" ].cast <int >() == 42 );
187189}
190+
191+ TEST_CASE (" Threads" ) {
192+ // Restart interpreter to ensure threads are not initialized
193+ py::finalize_interpreter ();
194+ py::initialize_interpreter ();
195+ REQUIRE_FALSE (has_pybind11_internals_static ());
196+
197+ constexpr auto num_threads = 10 ;
198+ auto locals = py::dict (" count" _a=0 );
199+
200+ {
201+ py::gil_scoped_release gil_release{};
202+ REQUIRE (has_pybind11_internals_static ());
203+
204+ auto threads = std::vector<std::thread>();
205+ for (auto i = 0 ; i < num_threads; ++i) {
206+ threads.emplace_back ([&]() {
207+ py::gil_scoped_acquire gil{};
208+ py::exec (" count += 1" , py::globals (), locals);
209+ });
210+ }
211+
212+ for (auto &thread : threads) {
213+ thread.join ();
214+ }
215+ }
216+
217+ REQUIRE (locals[" count" ].cast <int >() == num_threads);
218+ }
You can’t perform that action at this time.
0 commit comments