27 extern struct Dtool_PyTypedObject Dtool_TypedReferenceCount;
28 extern struct Dtool_PyTypedObject Dtool_AsyncFuture;
29 extern struct Dtool_PyTypedObject Dtool_PythonTask;
36 PythonTask(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);
117 set_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");
133 set_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;
157 PyObject *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);
189 set_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");
208 set_owner(PyObject *owner) {
210 if (owner != Py_None) {
211 PyObject *add = PyObject_GetAttrString(owner,
"_addTask");
212 PyObject *clear = PyObject_GetAttrString(owner,
"_clearTask");
214 if (add ==
nullptr || !PyCallable_Check(add) ||
215 clear ==
nullptr || !PyCallable_Check(clear)) {
216 Dtool_Raise_TypeError(
"owner object should have _addTask and _clearTask methods");
222 if (_owner !=
nullptr && _owner != Py_None && _state != S_inactive) {
223 unregister_from_owner();
230 if (_owner != Py_None && _state != S_inactive) {
240 PyObject *PythonTask::
242 nassertr(done(),
nullptr);
244 if (_exception ==
nullptr) {
246 Py_XINCREF(_exc_value);
249 _retrieved_exception =
true;
250 Py_INCREF(_exception);
251 Py_XINCREF(_exc_value);
252 Py_XINCREF(_exc_traceback);
253 PyErr_Restore(_exception, _exc_value, _exc_traceback);
283 __setattr__(PyObject *
self, PyObject *attr, PyObject *v) {
284 if (PyObject_GenericSetAttr(
self, attr, v) == 0) {
288 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
294 if (task_cat.is_debug()) {
295 PyObject *str = PyObject_Repr(v);
297 << *
this <<
": task."
298 #if PY_MAJOR_VERSION >= 3
299 << PyUnicode_AsUTF8(attr) <<
" = "
300 << PyUnicode_AsUTF8(str) <<
"\n";
302 << PyString_AsString(attr) <<
" = "
303 << PyString_AsString(str) <<
"\n";
308 return PyDict_SetItem(__dict__, attr, v);
318 __delattr__(PyObject *
self, PyObject *attr) {
319 if (PyObject_GenericSetAttr(
self, attr,
nullptr) == 0) {
323 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
329 if (PyDict_DelItem(__dict__, attr) == -1) {
331 #if PY_MAJOR_VERSION < 3
332 PyErr_Format(PyExc_AttributeError,
333 "'PythonTask' object has no attribute '%.400s'",
334 PyString_AS_STRING(attr));
336 PyErr_Format(PyExc_AttributeError,
337 "'PythonTask' object has no attribute '%U'",
352 PyObject *PythonTask::
353 __getattr__(PyObject *attr)
const {
359 PyObject *item = PyDict_GetItem(__dict__, attr);
361 if (item ==
nullptr) {
363 #if PY_MAJOR_VERSION < 3
364 PyErr_Format(PyExc_AttributeError,
365 "'PythonTask' object has no attribute '%.400s'",
366 PyString_AS_STRING(attr));
368 PyErr_Format(PyExc_AttributeError,
369 "'PythonTask' object has no attribute '%U'",
384 __traverse__(visitproc visit,
void *arg) {
421 return _function != Py_None;
429 AsyncTask::DoneStatus PythonTask::
431 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
433 PyGILState_STATE gstate;
434 gstate = PyGILState_Ensure();
437 DoneStatus result = do_python_task();
439 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
440 PyGILState_Release(gstate);
450 AsyncTask::DoneStatus PythonTask::
452 PyObject *result =
nullptr;
455 if (_future_done !=
nullptr) {
456 PyObject *is_done = PyObject_CallObject(_future_done,
nullptr);
457 if (!PyObject_IsTrue(is_done)) {
463 Py_DECREF(_future_done);
464 _future_done =
nullptr;
467 if (_generator ==
nullptr) {
469 nassertr(_function !=
nullptr, DS_interrupt);
471 PyObject *args = get_args();
472 result = PythonThread::call_python_func(_function, args);
475 if (result !=
nullptr && PyGen_Check(result)) {
478 if (task_cat.is_debug()) {
479 #if PY_MAJOR_VERSION >= 3
480 PyObject *str = PyObject_ASCII(_function);
482 << PyUnicode_AsUTF8(str) <<
" in " << *
this
483 <<
" yielded a generator.\n";
485 PyObject *str = PyObject_Repr(_function);
487 << PyString_AsString(str) <<
" in " << *
this
488 <<
" yielded a generator.\n";
495 #if PY_VERSION_HEX >= 0x03050000
496 }
else if (result !=
nullptr && Py_TYPE(result)->tp_as_async !=
nullptr) {
498 if (task_cat.is_debug()) {
499 PyObject *str = PyObject_ASCII(_function);
500 PyObject *str2 = PyObject_ASCII(result);
502 << PyUnicode_AsUTF8(str) <<
" in " << *
this
503 <<
" yielded an awaitable: " << PyUnicode_AsUTF8(str2) <<
"\n";
507 if (PyCoro_CheckExact(result)) {
512 unaryfunc await = Py_TYPE(result)->tp_as_async->am_await;
513 _generator = await(result);
521 if (_generator !=
nullptr) {
524 PyObject *func = PyObject_GetAttrString(_generator,
"send");
525 nassertr(func !=
nullptr, DS_interrupt);
526 result = PyObject_CallFunctionObjArgs(func, Py_None,
nullptr);
529 if (result ==
nullptr) {
533 Py_DECREF(_generator);
534 _generator =
nullptr;
536 #if PY_VERSION_HEX >= 0x03030000
537 if (_PyGen_FetchStopIterationValue(&result) == 0) {
539 if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
543 PyErr_Restore(
nullptr,
nullptr,
nullptr);
550 if (_function ==
nullptr) {
551 if (task_cat.is_debug()) {
553 << *
this <<
" received StopIteration from coroutine.\n";
556 Py_XDECREF(_exc_value);
560 }
else if (_function ==
nullptr) {
564 Py_XDECREF(_exception);
565 Py_XDECREF(_exc_value);
566 Py_XDECREF(_exc_traceback);
567 PyErr_Fetch(&_exception, &_exc_value, &_exc_traceback);
568 _retrieved_exception =
false;
570 if (task_cat.is_debug()) {
571 if (_exception !=
nullptr && Py_TYPE(_exception) == &PyType_Type) {
573 << *
this <<
" received " << ((PyTypeObject *)_exception)->tp_name <<
" from coroutine.\n";
576 << *
this <<
" received exception from coroutine.\n";
586 #if PY_VERSION_HEX >= 0x03050000
587 }
else if (result == Py_None && PyCoro_CheckExact(_generator)) {
593 }
else if (DtoolInstance_Check(result)) {
596 if (fut !=
nullptr) {
599 if (fut->is_task()) {
611 if (task_cat.is_debug()) {
613 << *
this <<
" is now awaiting <" << *fut <<
">.\n";
617 if (task_cat.is_debug()) {
619 << *
this <<
" would await <" << *fut <<
">, were it not already done.\n";
628 << *
this <<
" cannot await itself\n";
636 PyObject *check = PyObject_GetAttrString(result,
"_asyncio_future_blocking");
637 if (check !=
nullptr && check != Py_None) {
640 _future_done = PyObject_GetAttrString(result,
"done");
641 if (_future_done ==
nullptr || !PyCallable_Check(_future_done)) {
643 <<
"future.done is not callable\n";
646 #if PY_MAJOR_VERSION >= 3
647 if (task_cat.is_debug()) {
648 PyObject *str = PyObject_ASCII(result);
650 << *
this <<
" is now polling " << PyUnicode_AsUTF8(str) <<
".done()\n";
662 if (result ==
nullptr) {
663 if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
666 if (task_cat.is_debug()) {
668 <<
"SystemExit occurred in " << *
this <<
"\n";
672 <<
"Exception occurred in " << *
this <<
"\n";
677 if (result == Py_None || _ignore_return) {
682 #if PY_MAJOR_VERSION >= 3
683 if (PyLong_Check(result)) {
684 long retval = PyLong_AS_LONG(result);
686 if (PyInt_Check(result)) {
687 long retval = PyInt_AS_LONG(result);
692 Py_XDECREF(_generator);
693 _generator =
nullptr;
703 return (DoneStatus) retval;
718 PyMethodDef *meth =
nullptr;
719 if (PyCFunction_Check(result)) {
720 meth = ((PyCFunctionObject *)result)->m_ml;
721 #if PY_MAJOR_VERSION >= 3
722 }
else if (Py_TYPE(result) == &PyMethodDescr_Type) {
724 }
else if (strcmp(Py_TYPE(result)->tp_name,
"method_descriptor") == 0) {
726 meth = ((PyMethodDescrObject *)result)->d_method;
729 if (meth !=
nullptr && strcmp(meth->ml_name,
"done") == 0) {
734 std::ostringstream strm;
735 #if PY_MAJOR_VERSION >= 3
736 PyObject *str = PyObject_ASCII(result);
737 if (str ==
nullptr) {
738 str = PyUnicode_FromString(
"<repr error>");
741 << *
this <<
" returned " << PyUnicode_AsUTF8(str);
743 PyObject *str = PyObject_Repr(result);
744 if (str ==
nullptr) {
745 str = PyString_FromString(
"<repr error>");
748 << *
this <<
" returned " << PyString_AsString(str);
752 std::string message = strm.str();
753 nassert_raise(message);
766 AsyncTask::upon_birth(manager);
785 AsyncTask::upon_death(manager, clean_exit);
788 if (_future_done !=
nullptr) {
789 Py_DECREF(_future_done);
790 _future_done =
nullptr;
793 if (_upon_death != Py_None) {
794 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
796 PyGILState_STATE gstate;
797 gstate = PyGILState_Ensure();
800 call_function(_upon_death);
802 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
803 PyGILState_Release(gstate);
806 unregister_from_owner();
813 register_to_owner() {
814 if (_owner != Py_None && !_registered_to_owner) {
815 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
817 PyGILState_STATE gstate;
818 gstate = PyGILState_Ensure();
821 _registered_to_owner =
true;
822 call_owner_method(
"_addTask");
824 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
825 PyGILState_Release(gstate);
834 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 =
false;
844 call_owner_method(
"_clearTask");
846 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
847 PyGILState_Release(gstate);
857 call_owner_method(
const char *method_name) {
858 if (_owner != Py_None) {
859 PyObject *func = PyObject_GetAttrString(_owner, (
char *)method_name);
860 if (func ==
nullptr) {
862 <<
"Owner object added to " << *
this <<
" has no method "
863 << method_name <<
"().\n";
877 call_function(PyObject *
function) {
878 if (
function != Py_None) {
881 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.