|
19 | 19 |
|
20 | 20 | #include <functional>
|
21 | 21 | #include <memory>
|
| 22 | +#include <optional> |
22 | 23 | #include <utility>
|
23 | 24 |
|
24 | 25 | #include "arrow/buffer.h"
|
@@ -134,13 +135,15 @@ class ARROW_PYTHON_EXPORT PyAcquireGIL {
|
134 | 135 | // A RAII-style helper that releases the GIL until the end of a lexical block
|
135 | 136 | class ARROW_PYTHON_EXPORT PyReleaseGIL {
|
136 | 137 | public:
|
137 |
| - PyReleaseGIL() { saved_state_ = PyEval_SaveThread(); } |
138 |
| - |
139 |
| - ~PyReleaseGIL() { PyEval_RestoreThread(saved_state_); } |
| 138 | + PyReleaseGIL() : ptr_(PyEval_SaveThread(), &unique_ptr_deleter) {} |
140 | 139 |
|
141 | 140 | private:
|
142 |
| - PyThreadState* saved_state_; |
143 |
| - ARROW_DISALLOW_COPY_AND_ASSIGN(PyReleaseGIL); |
| 141 | + static void unique_ptr_deleter(PyThreadState* state) { |
| 142 | + if (state) { |
| 143 | + PyEval_RestoreThread(state); |
| 144 | + } |
| 145 | + } |
| 146 | + std::unique_ptr<PyThreadState, decltype(&unique_ptr_deleter)> ptr_; |
144 | 147 | };
|
145 | 148 |
|
146 | 149 | // A helper to call safely into the Python interpreter from arbitrary C++ code.
|
@@ -235,6 +238,48 @@ class ARROW_PYTHON_EXPORT OwnedRefNoGIL : public OwnedRef {
|
235 | 238 | }
|
236 | 239 | };
|
237 | 240 |
|
| 241 | +template <template <typename...> typename SmartPtr, typename... Ts> |
| 242 | +class SmartPtrNoGIL : public SmartPtr<Ts...> { |
| 243 | + using Base = SmartPtr<Ts...>; |
| 244 | + |
| 245 | + public: |
| 246 | + template <typename... Args> |
| 247 | + SmartPtrNoGIL(Args&&... args) : Base(std::forward<Args>(args)...) {} |
| 248 | + |
| 249 | + ~SmartPtrNoGIL() { reset(); } |
| 250 | + |
| 251 | + template <typename... Args> |
| 252 | + void reset(Args&&... args) { |
| 253 | + auto release_guard = optional_gil_release(); |
| 254 | + Base::reset(std::forward<Args>(args)...); |
| 255 | + } |
| 256 | + |
| 257 | + template <typename V> |
| 258 | + SmartPtrNoGIL& operator=(V&& v) { |
| 259 | + auto release_guard = optional_gil_release(); |
| 260 | + Base::operator=(std::forward<V>(v)); |
| 261 | + return *this; |
| 262 | + } |
| 263 | + |
| 264 | + private: |
| 265 | + // Only release the GIL if we own an object *and* the Python runtime is |
| 266 | + // valid *and* the GIL is held. |
| 267 | + std::optional<PyReleaseGIL> optional_gil_release() const { |
| 268 | + if (this->get() != nullptr && Py_IsInitialized() && PyGILState_Check()) { |
| 269 | + return PyReleaseGIL(); |
| 270 | + } |
| 271 | + return {}; |
| 272 | + } |
| 273 | +}; |
| 274 | + |
| 275 | +/// \brief A std::shared_ptr<T, ...> subclass that releases the GIL when destroying T |
| 276 | +template <typename... Ts> |
| 277 | +using SharedPtrNoGIL = SmartPtrNoGIL<std::shared_ptr, Ts...>; |
| 278 | + |
| 279 | +/// \brief A std::unique_ptr<T, ...> subclass that releases the GIL when destroying T |
| 280 | +template <typename... Ts> |
| 281 | +using UniquePtrNoGIL = SmartPtrNoGIL<std::unique_ptr, Ts...>; |
| 282 | + |
238 | 283 | template <typename Fn>
|
239 | 284 | struct BoundFunction;
|
240 | 285 |
|
|
0 commit comments