Open
Description
Let's say we have the following minimal example:
#include <pybind11/pybind11.h>
#include <pybind11/embed.h>
#include <iostream>
#include <thread>
static void thread()
{
pybind11::gil_scoped_acquire acquire;
std::cout << "Calling Python code from new thread" << std::endl;
}
int main()
{
pybind11::scoped_interpreter interp;
{
pybind11::gil_scoped_acquire acquire;
std::cout << "Calling Python code from main thread" << std::endl;
}
std::thread(thread).join();
return 0;
}
We would expect this to print:
Calling Python code from main thread
Calling Python code from new thread
But the cout
from the new thread is never reached because of a GIL deadlock. The reason this is happening is because PyEval_InitThreads()
, in addition to initializing threads, also acquires the GIL and never releases it. gil_scoped_acquire
calls PyEval_InitThreads()
the first time it runs, and it acquires the GIL a second time, so even when gil_scoped_acquire
is destructed, the GIL is still held.
If we change the example to this:
#include <pybind11/pybind11.h>
#include <pybind11/embed.h>
#include <iostream>
#include <thread>
static void thread()
{
pybind11::gil_scoped_acquire acquire;
std::cout << "Calling Python code from new thread" << std::endl;
}
static void init_threads()
{
if (!PyEval_ThreadsInitialized())
{
{
pybind11::gil_scoped_acquire acquire;
}
PyEval_SaveThread();
}
}
int main()
{
pybind11::scoped_interpreter interp;
init_threads();
{
pybind11::gil_scoped_acquire acquire;
std::cout << "Calling Python code from main thread" << std::endl;
}
std::thread(thread).join();
return 0;
}
then everything behaves as expected. Is this what I'm supposed to do, or is this a bug in pybind?
Metadata
Metadata
Assignees
Labels
No labels