File tree 3 files changed +39
-1
lines changed 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 {
1661
1661
class gil_scoped_release {
1662
1662
public:
1663
1663
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 ();
1664
1668
tstate = PyEval_SaveThread ();
1665
1669
if (disassoc) {
1666
- auto key = detail::get_internals () .tstate ;
1670
+ auto key = internals .tstate ;
1667
1671
#if PY_MAJOR_VERSION < 3
1668
1672
PyThread_delete_key_value (key);
1669
1673
#else
Original file line number Diff line number Diff line change 26
26
target_link_libraries (test_embed PRIVATE ${PYTHON_LIBRARIES} )
27
27
endif ()
28
28
29
+ find_package (Threads REQUIRED)
30
+ target_link_libraries (test_embed PUBLIC ${CMAKE_THREAD_LIBS_INIT} )
31
+
29
32
add_custom_target (cpptest COMMAND $<TARGET_FILE:test_embed>
30
33
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} )
31
34
add_dependencies (check cpptest)
Original file line number Diff line number Diff line change 1
1
#include < pybind11/embed.h>
2
2
#include < catch.hpp>
3
3
4
+ #include < thread>
5
+
4
6
namespace py = pybind11;
5
7
using namespace py ::literals;
6
8
@@ -185,3 +187,32 @@ TEST_CASE("Execution frame") {
185
187
py::exec (" var = dict(number=42)" );
186
188
REQUIRE (py::globals ()[" var" ][" number" ].cast <int >() == 42 );
187
189
}
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