27extern struct Dtool_PyTypedObject Dtool_TypedReferenceCount;
28extern struct Dtool_PyTypedObject Dtool_AsyncFuture;
29extern struct Dtool_PyTypedObject Dtool_PythonTask;
36PythonTask(PyObject *func_or_coro,
const std::string &name) :
42 _registered_to_owner(false),
45 _exc_traceback(nullptr),
47 _future_done(nullptr),
48 _ignore_return(false),
49 _retrieved_exception(false) {
51 nassertv(func_or_coro !=
nullptr);
52 if (func_or_coro == Py_None || PyCallable_Check(func_or_coro)) {
53 _function = func_or_coro;
55#if PY_VERSION_HEX >= 0x03050000
56 }
else if (PyCoro_CheckExact(func_or_coro)) {
58 _generator = func_or_coro;
59 Py_INCREF(_generator);
61 }
else if (PyGen_CheckExact(func_or_coro)) {
63 _generator = func_or_coro;
64 Py_INCREF(_generator);
66 nassert_raise(
"Invalid function passed to PythonTask");
69 set_args(Py_None,
true);
70 set_upon_death(Py_None);
73 __dict__ = PyDict_New();
75#if !defined(SIMPLE_THREADS) && defined(WITH_THREAD) && PY_VERSION_HEX < 0x03090000
90 if (_exception !=
nullptr && !_retrieved_exception) {
92 << *
this <<
" exception was never retrieved:\n";
93 PyErr_Restore(_exception, _exc_value, _exc_traceback);
95 PyErr_Restore(
nullptr,
nullptr,
nullptr);
98 _exc_traceback =
nullptr;
101 Py_XDECREF(_function);
104 Py_XDECREF(_exception);
105 Py_XDECREF(_exc_value);
106 Py_XDECREF(_exc_traceback);
107 Py_XDECREF(_generator);
109 Py_XDECREF(_upon_death);
117set_function(PyObject *function) {
118 Py_XDECREF(_function);
120 _function = function;
121 Py_INCREF(_function);
122 if (_function != Py_None && !PyCallable_Check(_function)) {
123 nassert_raise(
"Invalid function passed to PythonTask");
133set_args(PyObject *args,
bool append_task) {
137 if (args == Py_None) {
139 _args = PyTuple_New(0);
141 if (PySequence_Check(args)) {
142 _args = PySequence_Tuple(args);
146 if (_args ==
nullptr) {
147 nassert_raise(
"Invalid args passed to PythonTask");
148 _args = PyTuple_New(0);
151 _append_task = append_task;
157PyObject *PythonTask::
165 int num_args = PyTuple_GET_SIZE(_args);
166 PyObject *with_task = PyTuple_New(num_args + 1);
167 for (
int i = 0; i < num_args; ++i) {
168 PyObject *item = PyTuple_GET_ITEM(_args, i);
170 PyTuple_SET_ITEM(with_task, i, item);
175 PyTuple_SET_ITEM(with_task, num_args, self);
189set_upon_death(PyObject *upon_death) {
190 Py_XDECREF(_upon_death);
192 _upon_death = upon_death;
193 Py_INCREF(_upon_death);
194 if (_upon_death != Py_None && !PyCallable_Check(_upon_death)) {
195 nassert_raise(
"Invalid upon_death function passed to PythonTask");
208set_owner(PyObject *owner) {
210 if (owner != Py_None) {
211 PyObject *add = PyObject_GetAttrString(owner,
"_addTask");
213 PyObject *clear = PyObject_GetAttrString(owner,
"_clearTask");
216 bool valid_add =
false;
217 if (add !=
nullptr) {
218 valid_add = PyCallable_Check(add);
221 bool valid_clear =
false;
222 if (clear !=
nullptr) {
223 valid_clear = PyCallable_Check(clear);
227 if (!valid_add || !valid_clear) {
228 Dtool_Raise_TypeError(
"owner object should have _addTask and _clearTask methods");
234 if (_owner !=
nullptr && _owner != Py_None && _state != S_inactive) {
235 unregister_from_owner();
242 if (_owner != Py_None && _state != S_inactive) {
252PyObject *PythonTask::
254 nassertr(done(),
nullptr);
256 if (_exception ==
nullptr) {
258 Py_XINCREF(_exc_value);
261 _retrieved_exception =
true;
262 Py_INCREF(_exception);
263 Py_XINCREF(_exc_value);
264 Py_XINCREF(_exc_traceback);
265 PyErr_Restore(_exception, _exc_value, _exc_traceback);
295__setattr__(PyObject *self, PyObject *attr, PyObject *v) {
296 if (PyObject_GenericSetAttr(self, attr, v) == 0) {
300 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
306 if (task_cat.is_debug()) {
307 PyObject *str = PyObject_Repr(v);
309 << *
this <<
": task."
310#if PY_MAJOR_VERSION >= 3
311 << PyUnicode_AsUTF8(attr) <<
" = "
312 << PyUnicode_AsUTF8(str) <<
"\n";
314 << PyString_AsString(attr) <<
" = "
315 << PyString_AsString(str) <<
"\n";
320 return PyDict_SetItem(__dict__, attr, v);
330__delattr__(PyObject *self, PyObject *attr) {
331 if (PyObject_GenericSetAttr(self, attr,
nullptr) == 0) {
335 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
341 if (PyDict_DelItem(__dict__, attr) == -1) {
343#if PY_MAJOR_VERSION < 3
344 PyErr_Format(PyExc_AttributeError,
345 "'PythonTask' object has no attribute '%.400s'",
346 PyString_AS_STRING(attr));
348 PyErr_Format(PyExc_AttributeError,
349 "'PythonTask' object has no attribute '%U'",
364PyObject *PythonTask::
365__getattr__(PyObject *attr)
const {
371 PyObject *item = PyDict_GetItem(__dict__, attr);
373 if (item ==
nullptr) {
375#if PY_MAJOR_VERSION < 3
376 PyErr_Format(PyExc_AttributeError,
377 "'PythonTask' object has no attribute '%.400s'",
378 PyString_AS_STRING(attr));
380 PyErr_Format(PyExc_AttributeError,
381 "'PythonTask' object has no attribute '%U'",
396__traverse__(visitproc visit,
void *arg) {
433 return _function != Py_None;
441AsyncTask::DoneStatus PythonTask::
443#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
445 PyGILState_STATE gstate;
446 gstate = PyGILState_Ensure();
449 DoneStatus result = do_python_task();
451#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
452 PyGILState_Release(gstate);
462AsyncTask::DoneStatus PythonTask::
464 PyObject *result =
nullptr;
467 if (_future_done !=
nullptr) {
468 PyObject *is_done = PyObject_CallObject(_future_done,
nullptr);
469 if (!PyObject_IsTrue(is_done)) {
475 Py_DECREF(_future_done);
476 _future_done =
nullptr;
479 if (_generator ==
nullptr) {
481 nassertr(_function !=
nullptr, DS_interrupt);
483 PyObject *args = get_args();
484 result = PythonThread::call_python_func(_function, args);
487 if (result !=
nullptr && PyGen_Check(result)) {
490 if (task_cat.is_debug()) {
491#if PY_MAJOR_VERSION >= 3
492 PyObject *str = PyObject_ASCII(_function);
494 << PyUnicode_AsUTF8(str) <<
" in " << *
this
495 <<
" yielded a generator.\n";
497 PyObject *str = PyObject_Repr(_function);
499 << PyString_AsString(str) <<
" in " << *
this
500 <<
" yielded a generator.\n";
507#if PY_VERSION_HEX >= 0x03050000
508 }
else if (result !=
nullptr && Py_TYPE(result)->tp_as_async !=
nullptr) {
510 if (task_cat.is_debug()) {
511 PyObject *str = PyObject_ASCII(_function);
512 PyObject *str2 = PyObject_ASCII(result);
514 << PyUnicode_AsUTF8(str) <<
" in " << *
this
515 <<
" yielded an awaitable: " << PyUnicode_AsUTF8(str2) <<
"\n";
519 if (PyCoro_CheckExact(result)) {
524 unaryfunc await = Py_TYPE(result)->tp_as_async->am_await;
525 _generator = await(result);
533 if (_generator !=
nullptr) {
536 PyObject *func = PyObject_GetAttrString(_generator,
"send");
537 nassertr(func !=
nullptr, DS_interrupt);
538 result = PyObject_CallFunctionObjArgs(func, Py_None,
nullptr);
541 if (result ==
nullptr) {
545 Py_DECREF(_generator);
546 _generator =
nullptr;
548#if PY_VERSION_HEX >= 0x030D0000
550 if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
551 PyObject *exc = PyErr_GetRaisedException();
552 result = ((PyStopIterationObject *)exc)->value;
553 if (result ==
nullptr) {
558#elif PY_VERSION_HEX >= 0x03030000
559 if (_PyGen_FetchStopIterationValue(&result) == 0) {
561 if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
565 PyErr_Restore(
nullptr,
nullptr,
nullptr);
572 if (_function ==
nullptr) {
573 if (task_cat.is_debug()) {
575 << *
this <<
" received StopIteration from coroutine.\n";
578 Py_XDECREF(_exc_value);
582 }
else if (_function ==
nullptr) {
586 Py_XDECREF(_exception);
587 Py_XDECREF(_exc_value);
588 Py_XDECREF(_exc_traceback);
589 PyErr_Fetch(&_exception, &_exc_value, &_exc_traceback);
590 _retrieved_exception =
false;
592 if (task_cat.is_debug()) {
593 if (_exception !=
nullptr && Py_TYPE(_exception) == &PyType_Type) {
595 << *
this <<
" received " << ((PyTypeObject *)_exception)->tp_name <<
" from coroutine.\n";
598 << *
this <<
" received exception from coroutine.\n";
608#if PY_VERSION_HEX >= 0x03050000
609 }
else if (result == Py_None && PyCoro_CheckExact(_generator)) {
615 }
else if (DtoolInstance_Check(result)) {
618 if (fut !=
nullptr) {
621 if (fut->is_task()) {
633 if (task_cat.is_debug()) {
635 << *
this <<
" is now awaiting <" << *fut <<
">.\n";
639 if (task_cat.is_debug()) {
641 << *
this <<
" would await <" << *fut <<
">, were it not already done.\n";
650 << *
this <<
" cannot await itself\n";
658 PyObject *check = PyObject_GetAttrString(result,
"_asyncio_future_blocking");
659 if (check !=
nullptr && check != Py_None) {
662 _future_done = PyObject_GetAttrString(result,
"done");
663 if (_future_done ==
nullptr || !PyCallable_Check(_future_done)) {
665 <<
"future.done is not callable\n";
668#if PY_MAJOR_VERSION >= 3
669 if (task_cat.is_debug()) {
670 PyObject *str = PyObject_ASCII(result);
672 << *
this <<
" is now polling " << PyUnicode_AsUTF8(str) <<
".done()\n";
684 if (result ==
nullptr) {
685 if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
688 if (task_cat.is_debug()) {
690 <<
"SystemExit occurred in " << *
this <<
"\n";
694 <<
"Exception occurred in " << *
this <<
"\n";
699 if (result == Py_None || _ignore_return) {
704#if PY_MAJOR_VERSION >= 3
705 if (PyLong_Check(result)) {
706 long retval = PyLong_AS_LONG(result);
708 if (PyInt_Check(result)) {
709 long retval = PyInt_AS_LONG(result);
714 Py_XDECREF(_generator);
715 _generator =
nullptr;
725 return (DoneStatus) retval;
740 PyMethodDef *meth =
nullptr;
741 if (PyCFunction_Check(result)) {
742 meth = ((PyCFunctionObject *)result)->m_ml;
743#if PY_MAJOR_VERSION >= 3
744 }
else if (Py_TYPE(result) == &PyMethodDescr_Type) {
746 }
else if (strcmp(Py_TYPE(result)->tp_name,
"method_descriptor") == 0) {
748 meth = ((PyMethodDescrObject *)result)->d_method;
751 if (meth !=
nullptr && strcmp(meth->ml_name,
"done") == 0) {
756 std::ostringstream strm;
757#if PY_MAJOR_VERSION >= 3
758 PyObject *str = PyObject_ASCII(result);
759 if (str ==
nullptr) {
760 str = PyUnicode_FromString(
"<repr error>");
763 << *
this <<
" returned " << PyUnicode_AsUTF8(str);
765 PyObject *str = PyObject_Repr(result);
766 if (str ==
nullptr) {
767 str = PyString_FromString(
"<repr error>");
770 << *
this <<
" returned " << PyString_AsString(str);
774 std::string message = strm.str();
775 nassert_raise(message);
788 AsyncTask::upon_birth(manager);
807 AsyncTask::upon_death(manager, clean_exit);
810 if (_future_done !=
nullptr) {
811 Py_DECREF(_future_done);
812 _future_done =
nullptr;
815 if (_upon_death != Py_None) {
816#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
818 PyGILState_STATE gstate;
819 gstate = PyGILState_Ensure();
822 call_function(_upon_death);
824#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
825 PyGILState_Release(gstate);
828 unregister_from_owner();
836 if (_owner != Py_None && !_registered_to_owner) {
837#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
839 PyGILState_STATE gstate;
840 gstate = PyGILState_Ensure();
843 _registered_to_owner =
true;
844 call_owner_method(
"_addTask");
846#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
847 PyGILState_Release(gstate);
856unregister_from_owner() {
858 if (_owner != Py_None && _registered_to_owner) {
859#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
861 PyGILState_STATE gstate;
862 gstate = PyGILState_Ensure();
865 _registered_to_owner =
false;
866 call_owner_method(
"_clearTask");
868#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
869 PyGILState_Release(gstate);
879call_owner_method(
const char *method_name) {
880 if (_owner != Py_None) {
881 PyObject *func = PyObject_GetAttrString(_owner, (
char *)method_name);
882 if (func ==
nullptr) {
884 <<
"Owner object added to " << *
this <<
" has no method "
885 << method_name <<
"().\n";
899call_function(PyObject *function) {
900 if (function != Py_None) {
903 PyObject *result = PyObject_CallFunctionObjArgs(function, self,
nullptr);
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class represents a thread-safe handle to a promised future result of an asynchronous operation,...
bool add_waiting_task(AsyncTask *task)
Indicates that the given task is waiting for this future to complete.
bool done() const
Returns true if the future is done or has been cancelled.
A class to manage a loose queue of isolated tasks, which can be performed either synchronously (in th...
This class represents a concrete task performed by an AsyncManager.
is_alive
Returns true if the task is currently active or sleeping on some task chain, meaning that it will be ...
TypeHandle is the identifier used to differentiate C++ class types.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PyObject * DTool_CreatePyInstance(const T *obj, bool memory_rules)
These functions wrap a pointer for a class that defines get_type_handle().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.