22 #include <ucxx/python/future.h>
23 #include <ucxx/python/python_future_task_collector.h>
37 template <
typename ReturnType,
typename... TaskArgs>
40 std::packaged_task<ReturnType(TaskArgs...)>
_task{};
41 std::function<PyObject*(ReturnType)>
59 void setResult(
const ReturnType result)
62 if (
_handle ==
nullptr)
throw std::runtime_error(
"Invalid object or already released");
64 PyGILState_STATE state = PyGILState_Ensure();
65 ucxx::python::future_set_result_with_event_loop(
67 PyGILState_Release(state);
79 void setPythonException(PyObject* pythonException,
const std::string& message)
81 if (
_handle ==
nullptr)
throw std::runtime_error(
"Invalid object or already released");
83 PyGILState_STATE state = PyGILState_Ensure();
84 ucxx::python::future_set_exception_with_event_loop(
86 PyGILState_Release(state);
97 void setException(
const std::exception& exception)
101 }
catch (
const std::bad_alloc& e) {
102 setPythonException(PyExc_MemoryError, e.what());
103 }
catch (
const std::bad_cast& e) {
104 setPythonException(PyExc_TypeError, e.what());
105 }
catch (
const std::bad_typeid& e) {
106 setPythonException(PyExc_TypeError, e.what());
107 }
catch (
const std::domain_error& e) {
108 setPythonException(PyExc_ValueError, e.what());
109 }
catch (
const std::invalid_argument& e) {
110 setPythonException(PyExc_ValueError, e.what());
111 }
catch (
const std::ios_base::failure& e) {
112 setPythonException(PyExc_IOError, e.what());
113 }
catch (
const std::out_of_range& e) {
114 setPythonException(PyExc_IndexError, e.what());
115 }
catch (
const std::overflow_error& e) {
116 setPythonException(PyExc_OverflowError, e.what());
117 }
catch (
const std::range_error& e) {
118 setPythonException(PyExc_ArithmeticError, e.what());
119 }
catch (
const std::underflow_error& e) {
120 setPythonException(PyExc_ArithmeticError, e.what());
121 }
catch (
const std::exception& e) {
122 setPythonException(PyExc_RuntimeError, e.what());
124 setPythonException(PyExc_RuntimeError,
"Unknown exception");
148 std::function<PyObject*(ReturnType)> pythonConvert,
149 PyObject* asyncioEventLoop,
150 std::launch launchPolicy = std::launch::async)
151 :
_task{std::move(task)},
154 _handle{
ucxx::python::create_python_future_with_event_loop(asyncioEventLoop)},
155 _future{std::async(launchPolicy, [this]() {
156 std::future<ReturnType> result = this->
_task.get_future();
159 const ReturnType r = result.get();
162 }
catch (std::exception& e) {
163 this->setException(e);
169 PythonFutureTask(
const PythonFutureTask&) =
delete;
170 PythonFutureTask& operator=(PythonFutureTask
const&) =
delete;
211 if (_handle ==
nullptr)
throw std::runtime_error(
"Invalid object or already released");
232 if (_handle ==
nullptr)
throw std::runtime_error(
"Invalid object or already released");
250 if (_handle ==
nullptr)
throw std::runtime_error(
"Invalid object or already released");
252 return std::exchange(_handle,
nullptr);
264 template <
typename ReturnType,
typename... TaskArgs>
265 class PythonFutureTask :
public std::enable_shared_from_this<PythonFutureTask<ReturnType>> {
290 std::function<PyObject*(ReturnType)> pythonConvert,
291 PyObject* asyncioEventLoop,
292 std::launch launchPolicy = std::launch::async)
293 : _detail(std::make_unique<detail::
PythonFutureTask<ReturnType, TaskArgs...>>(
294 std::move(task), std::move(pythonConvert), asyncioEventLoop, launchPolicy))
325 [[nodiscard]] std::future<ReturnType>&
getFuture() {
return _detail->getFuture(); }
341 [[nodiscard]] PyObject*
getHandle() {
return _detail->getHandle(); }
354 [[nodiscard]] PyObject*
release() {
return _detail->release(); }
void push(PyObject *handle)
static PythonFutureTaskCollector & get()
User-facing bridge of C++ and Python futures.
Definition: python_future_task.h:265
PyObject * release()
Get the underlying future PyObject* handle and release ownership.
Definition: python_future_task.h:354
std::future< ReturnType > & getFuture()
Get the C++ future.
Definition: python_future_task.h:325
PythonFutureTask & operator=(PythonFutureTask &&o)=default
The move operator.
PyObject * getHandle()
Get the underlying future PyObject* handle but does not release ownership.
Definition: python_future_task.h:341
PythonFutureTask(std::packaged_task< ReturnType(TaskArgs...)> task, std::function< PyObject *(ReturnType)> pythonConvert, PyObject *asyncioEventLoop, std::launch launchPolicy=std::launch::async)
Construct a Python future backed by C++ std::packaged_task.
Definition: python_future_task.h:289
PythonFutureTask(PythonFutureTask &&o)=default
The move constructor.
A bridge of C++ and Python futures.
Definition: python_future_task.h:38
PythonFutureTask & operator=(PythonFutureTask &&o)=default
The move operator.
std::function< PyObject *(ReturnType)> _pythonConvert
Function to convert the C++ result into Python value.
Definition: python_future_task.h:42
std::future< ReturnType > _future
The C++ future containing the task result.
Definition: python_future_task.h:45
PythonFutureTask(std::packaged_task< ReturnType(TaskArgs...)> task, std::function< PyObject *(ReturnType)> pythonConvert, PyObject *asyncioEventLoop, std::launch launchPolicy=std::launch::async)
Construct a Python future backed by C++ std::packaged_task.
Definition: python_future_task.h:147
std::future< ReturnType > & getFuture()
Get the C++ future.
Definition: python_future_task.h:209
PyObject * _handle
The handle to the Python future.
Definition: python_future_task.h:44
PyObject * _asyncioEventLoop
The handle to the Python asyncio event loop.
Definition: python_future_task.h:43
std::packaged_task< ReturnType(TaskArgs...)> _task
The user-defined C++ task to run.
Definition: python_future_task.h:40
PyObject * release()
Get the underlying future PyObject* handle and release ownership.
Definition: python_future_task.h:248
~PythonFutureTask()
Python future destructor.
Definition: python_future_task.h:200
PythonFutureTask(PythonFutureTask &&o)=default
The move constructor.
PyObject * getHandle()
Get the underlying future PyObject* handle but does not release ownership.
Definition: python_future_task.h:230