Skip to content
This repository has been archived by the owner on Mar 10, 2022. It is now read-only.

Commit

Permalink
core: wrestle with CPython, allowing pthreads to unwind
Browse files Browse the repository at this point in the history
Also correctly destruct the py::gil_scoped_release first.

Resolves #35, finally.
  • Loading branch information
tmplt committed May 2, 2018
1 parent 90ae351 commit 049df29
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 6 deletions.
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
branch = master
[submodule "lib/pybind11"]
path = lib/pybind11
url = https://github.com/pybind/pybind11.git
branch = master
url = https://github.com/Tmplt/pybind11.git
branch = fix-scoped-acquire-for-python3
29 changes: 27 additions & 2 deletions src/core/plugin_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,38 @@ plugin_handler::~plugin_handler()

void plugin_handler::async_search()
{
/* Ensure pybind internals are initialized. */
py::get_shared_data("");

for (const auto &m : plugins_) {
threads_.emplace_back([&m, wanted = wanted_, instance = this]() {
/* Required whenever we need to run anything Python. */
py::gil_scoped_acquire gil;
auto gil = std::make_unique<py::gil_scoped_acquire>();

/*
* We have to go manual here. Normally, when unwinding on pthread exit,
* Python operations may be performed without holding the GIL, leading to a segfault.
*
* This fix may only work on Linux, since abi::__forced_unwind is an implementation detail.
*/
py::object func = m.attr("find");
py::tuple args = py::make_tuple(wanted, instance);

try {
m.attr("find")(wanted, instance);
PyObject_Call(func.ptr(), args.ptr(), nullptr);
} catch (abi::__forced_unwind&) {
/*
* Forced stack unwinding at thread exit —
* if Python is shutting down, don't clean up Python state.
*/
if (!Py_IsInitialized()) {
args.release();
func.release();
gil.release();
}

/* Important rethrow! But what do we catch? */
throw;
} catch (const py::error_already_set &err) {
instance->log(log_level::err, fmt::format("module '{}' did something wrong: {}; ignoring...",
m.attr("__name__").cast<string>(), err.what()));
Expand Down
2 changes: 1 addition & 1 deletion src/core/plugin_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ class __attribute__ ((visibility("hidden"))) plugin_handler {

/* Python-specific; do not change the order of this. */
py::scoped_interpreter interp;
std::unique_ptr<py::gil_scoped_release> nogil;
vector<std::thread> threads_;
vector<py::module> plugins_;
std::unique_ptr<py::gil_scoped_release> nogil;
};

/* ns bookwyrm::core */
Expand Down

0 comments on commit 049df29

Please sign in to comment.