Fix GIL release and acquire when embedding the interpreter

Fixes a race condition when multiple threads try to acquire the GIL
before `detail::internals` have been initialized. `gil_scoped_release`
is now tasked with initializing `internals` (guaranteed single-threaded)
to ensure the safety of subsequent `acquire` calls from multiple threads.
This commit is contained in:
Dean Moldovan
2017-06-20 23:50:51 +02:00
parent f42af24a7d
commit 2d6116b53f
3 changed files with 39 additions and 1 deletions

View File

@@ -1,6 +1,8 @@
#include <pybind11/embed.h>
#include <catch.hpp>
#include <thread>
namespace py = pybind11;
using namespace py::literals;
@@ -185,3 +187,32 @@ TEST_CASE("Execution frame") {
py::exec("var = dict(number=42)");
REQUIRE(py::globals()["var"]["number"].cast<int>() == 42);
}
TEST_CASE("Threads") {
// Restart interpreter to ensure threads are not initialized
py::finalize_interpreter();
py::initialize_interpreter();
REQUIRE_FALSE(has_pybind11_internals_static());
constexpr auto num_threads = 10;
auto locals = py::dict("count"_a=0);
{
py::gil_scoped_release gil_release{};
REQUIRE(has_pybind11_internals_static());
auto threads = std::vector<std::thread>();
for (auto i = 0; i < num_threads; ++i) {
threads.emplace_back([&]() {
py::gil_scoped_acquire gil{};
py::exec("count += 1", py::globals(), locals);
});
}
for (auto &thread : threads) {
thread.join();
}
}
REQUIRE(locals["count"].cast<int>() == num_threads);
}