-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Provide overload similar to return_value_policy to unlock the GIL #625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This cleans up the previous commit slightly by further reducing the function call arguments to a single struct (containing the function_record, arguments vector, and parent). Although this doesn't currently change anything, it does allow for future functionality to have a place for precalls to store temporary objects that need to be destroyed after a function call (whether or not the call succeeds). As a concrete example, with this change pybind#625 could be easily implemented (I think) by adding a std::unique_ptr<gil_scoped_release> member to the `function_call` struct with a precall that actually constructs it. Without this, the precall can't do that: the postcall won't be invoked if the call throws an exception. This doesn't seems to affect the .so size noticeably (either way).
One of the big issues with this is that it would have to be in the core function call dispatch code, but adding it would increase the object size of all function calls. I just pushed a change to #611 that might aid with this, however, by allowing this to happen through the precall mechanism without impacting other functions. See the commit message for how I think it could easily work without impacting functions that don't use it if you want to take a stab at implementing it. |
It does seem that the precall/postcall mechanism would be ideal for implementing such a thing. Assuming that having an extra |
This cleans up the previous commit slightly by further reducing the function call arguments to a single struct (containing the function_record, arguments vector, and parent). Although this doesn't currently change anything, it does allow for future functionality to have a place for precalls to store temporary objects that need to be destroyed after a function call (whether or not the call succeeds). As a concrete example, with this change pybind#625 could be easily implemented (I think) by adding a std::unique_ptr<gil_scoped_release> member to the `function_call` struct with a precall that actually constructs it. Without this, the precall can't do that: the postcall won't be invoked if the call throws an exception. This doesn't seems to affect the .so size noticeably (either way).
This cleans up the previous commit slightly by further reducing the function call arguments to a single struct (containing the function_record, arguments vector, and parent). Although this doesn't currently change anything, it does allow for future functionality to have a place for precalls to store temporary objects that need to be destroyed after a function call (whether or not the call succeeds). As a concrete example, with this change pybind#625 could be easily implemented (I think) by adding a std::unique_ptr<gil_scoped_release> member to the `function_call` struct with a precall that actually constructs it. Without this, the precall can't do that: the postcall won't be invoked if the call throws an exception. This doesn't seems to affect the .so size noticeably (either way).
This cleans up the previous commit slightly by further reducing the function call arguments to a single struct (containing the function_record, arguments vector, and parent). Although this doesn't currently change anything, it does allow for future functionality to have a place for precalls to store temporary objects that need to be destroyed after a function call (whether or not the call succeeds). As a concrete example, with this change #625 could be easily implemented (I think) by adding a std::unique_ptr<gil_scoped_release> member to the `function_call` struct with a precall that actually constructs it. Without this, the precall can't do that: the postcall won't be invoked if the call throws an exception. This doesn't seems to affect the .so size noticeably (either way).
I looked a little bit at your commit, and I don't believe that mechanism would be quite what one would need. It would release the gil too early, as it would presumably be needed for any arg casting that might happen?. Same thing with the return value. |
Hmm, I think you're right: the precall would be fine for initializing it, as long as it was the last precall, but it needs to expire between the function call and the casting of the return value. (There's also another problem added since that commit: with #643, the function_call might not expire right away: there are overload cases where it is saved to try again a second time). Another idea that might work: a wrapper generator so that you could write something like: m.def("f", gil_released(&f))
|
Alright, so I wrote a wrapper function that seems to do the job (inspired by http://stackoverflow.com/questions/30679445/python-like-c-decorators and the cpp_function implementation) for my use cases: namespace pybind11 {
// normal functions
template <typename Return, typename... Args>
std::function<Return(Args...)> release_gil(Return (*f)(Args ...) PYBIND11_NOEXCEPT_SPECIFIER) {
return [f](Args... args) -> Return {
gil_scoped_release r;
return f(args...);
};
}
// member functions (non-const)
template <typename Return, typename Class, typename... Args>
std::function<Return(Class*, Args...)> release_gil(Return (Class::*f)(Args ...) PYBIND11_NOEXCEPT_SPECIFIER) {
return [f](Class *c, Args... args) -> Return {
gil_scoped_release r;
return (c->*f)(args...);
};
}
// member functions (const)
template <typename Return, typename Class, typename... Args>
std::function<Return(Class*, Args...)> release_gil(Return (Class::*f)(Args ...) const PYBIND11_NOEXCEPT_SPECIFIER) {
return [f](const Class *c, Args... args) -> Return {
gil_scoped_release r;
return (c->*f)(args...);
};
}
} // namespace pybind11 |
.. ha, I hadn't seen your comment yet, but that's exactly what I was thinking too. :) |
I think this would be worth submitting as a PR: it's a nice feature that doesn't impose any cost if not using it. Some comments:
|
I agree that it would be great to be builtin to pybind... if nobody else does it, I will try to get to it in March, as I've got some tight deadlines that I'm trying to meet. |
C++ allows it: void foo() {
return void();
}
void bar() {
return foo();
} Makes generic code nice and consistent. |
BTW: all the PYBIND11_NOEXCEPT_ stuff is gone now; it was experimental for C++17, but didn't work properly. (And the replacement is much cleaner). |
Resolved as part of #740. The GIL can be released using: m.def("foo", foo, py::call_guard<py::gil_scoped_release>()); |
It would be ideal to not have to create a lambda each time one wants to unlock the gil:
Instead, it seems like an ideal API would be something like:
The text was updated successfully, but these errors were encountered: