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) ||
57 PyGen_CheckExact(func_or_coro) ||
58 PyType_IsSubtype(Py_TYPE(func_or_coro), &PyCoro_Type)) {
60 _generator = func_or_coro;
61 Py_INCREF(_generator);
63 }
else if (PyGen_CheckExact(func_or_coro)) {
65 _generator = func_or_coro;
66 Py_INCREF(_generator);
69 nassert_raise(
"Invalid function passed to PythonTask");
72 set_args(Py_None,
true);
73 set_upon_death(Py_None);
76 __dict__ = PyDict_New();
78#if !defined(SIMPLE_THREADS) && defined(WITH_THREAD) && PY_VERSION_HEX < 0x03090000
93 if (_exception !=
nullptr && !_retrieved_exception) {
95 << *
this <<
" exception was never retrieved:\n";
96 PyErr_Restore(_exception, _exc_value, _exc_traceback);
98 PyErr_Restore(
nullptr,
nullptr,
nullptr);
100 _exc_value =
nullptr;
101 _exc_traceback =
nullptr;
104 Py_XDECREF(_function);
107 Py_XDECREF(_exception);
108 Py_XDECREF(_exc_value);
109 Py_XDECREF(_exc_traceback);
110 Py_XDECREF(_generator);
112 Py_XDECREF(_upon_death);
120set_function(PyObject *function) {
121 Py_XDECREF(_function);
123 _function = function;
124 Py_INCREF(_function);
125 if (_function != Py_None && !PyCallable_Check(_function)) {
126 nassert_raise(
"Invalid function passed to PythonTask");
136set_args(PyObject *args,
bool append_task) {
140 if (args == Py_None) {
142 _args = PyTuple_New(0);
144 if (PySequence_Check(args)) {
145 _args = PySequence_Tuple(args);
149 if (_args ==
nullptr) {
150 nassert_raise(
"Invalid args passed to PythonTask");
151 _args = PyTuple_New(0);
154 _append_task = append_task;
160PyObject *PythonTask::
168 int num_args = PyTuple_GET_SIZE(_args);
169 PyObject *with_task = PyTuple_New(num_args + 1);
170 for (
int i = 0; i < num_args; ++i) {
171 PyObject *item = PyTuple_GET_ITEM(_args, i);
173 PyTuple_SET_ITEM(with_task, i, item);
178 PyTuple_SET_ITEM(with_task, num_args, self);
192set_upon_death(PyObject *upon_death) {
193 Py_XDECREF(_upon_death);
195 _upon_death = upon_death;
196 Py_INCREF(_upon_death);
197 if (_upon_death != Py_None && !PyCallable_Check(_upon_death)) {
198 nassert_raise(
"Invalid upon_death function passed to PythonTask");
211set_owner(PyObject *owner) {
213 if (owner != Py_None) {
214 PyObject *add = PyObject_GetAttrString(owner,
"_addTask");
216 PyObject *clear = PyObject_GetAttrString(owner,
"_clearTask");
219 bool valid_add =
false;
220 if (add !=
nullptr) {
221 valid_add = PyCallable_Check(add);
224 bool valid_clear =
false;
225 if (clear !=
nullptr) {
226 valid_clear = PyCallable_Check(clear);
230 if (!valid_add || !valid_clear) {
231 Dtool_Raise_TypeError(
"owner object should have _addTask and _clearTask methods");
237 if (_owner !=
nullptr && _owner != Py_None && _state != S_inactive) {
238 unregister_from_owner();
245 if (_owner != Py_None && _state != S_inactive) {
255PyObject *PythonTask::
257 nassertr(done(),
nullptr);
259 if (_exception ==
nullptr) {
261 Py_XINCREF(_exc_value);
264 _retrieved_exception =
true;
265 Py_INCREF(_exception);
266 Py_XINCREF(_exc_value);
267 Py_XINCREF(_exc_traceback);
268 PyErr_Restore(_exception, _exc_value, _exc_traceback);
298__setattr__(PyObject *self, PyObject *attr, PyObject *v) {
299 if (PyObject_GenericSetAttr(self, attr, v) == 0) {
303 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
309 if (task_cat.is_debug()) {
310 PyObject *str = PyObject_Repr(v);
312 << *
this <<
": task."
313#if PY_MAJOR_VERSION >= 3
314 << PyUnicode_AsUTF8(attr) <<
" = "
315 << PyUnicode_AsUTF8(str) <<
"\n";
317 << PyString_AsString(attr) <<
" = "
318 << PyString_AsString(str) <<
"\n";
323 return PyDict_SetItem(__dict__, attr, v);
333__delattr__(PyObject *self, PyObject *attr) {
334 if (PyObject_GenericSetAttr(self, attr,
nullptr) == 0) {
338 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
344 if (PyDict_DelItem(__dict__, attr) == -1) {
346#if PY_MAJOR_VERSION < 3
347 PyErr_Format(PyExc_AttributeError,
348 "'PythonTask' object has no attribute '%.400s'",
349 PyString_AS_STRING(attr));
351 PyErr_Format(PyExc_AttributeError,
352 "'PythonTask' object has no attribute '%U'",
367PyObject *PythonTask::
368__getattr__(PyObject *attr)
const {
374#if PY_VERSION_HEX >= 0x030D00A1
375 PyObject *item =
nullptr;
376 if (PyDict_GetItemRef(__dict__, attr, &item) == 0) {
378 PyErr_Format(PyExc_AttributeError,
379 "'PythonTask' object has no attribute '%U'",
385 PyObject *item = PyDict_GetItem(__dict__, attr);
387 if (item ==
nullptr) {
389#if PY_MAJOR_VERSION < 3
390 PyErr_Format(PyExc_AttributeError,
391 "'PythonTask' object has no attribute '%.400s'",
392 PyString_AS_STRING(attr));
394 PyErr_Format(PyExc_AttributeError,
395 "'PythonTask' object has no attribute '%U'",
411__traverse__(visitproc visit,
void *arg) {
448 return _function != Py_None;
456AsyncTask::DoneStatus PythonTask::
458#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
460 PyGILState_STATE gstate;
461 gstate = PyGILState_Ensure();
464 DoneStatus result = do_python_task();
466#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
467 PyGILState_Release(gstate);
477AsyncTask::DoneStatus PythonTask::
479 PyObject *result =
nullptr;
482 if (_future_done !=
nullptr) {
483 PyObject *is_done = PyObject_CallObject(_future_done,
nullptr);
484 if (!PyObject_IsTrue(is_done)) {
490 Py_DECREF(_future_done);
491 _future_done =
nullptr;
494 if (_generator ==
nullptr) {
496 nassertr(_function !=
nullptr, DS_interrupt);
498 PyObject *args = get_args();
499 result = PythonThread::call_python_func(_function, args);
502 if (result !=
nullptr && PyGen_Check(result)) {
505 if (task_cat.is_debug()) {
506#if PY_MAJOR_VERSION >= 3
507 PyObject *str = PyObject_ASCII(_function);
509 << PyUnicode_AsUTF8(str) <<
" in " << *
this
510 <<
" yielded a generator.\n";
512 PyObject *str = PyObject_Repr(_function);
514 << PyString_AsString(str) <<
" in " << *
this
515 <<
" yielded a generator.\n";
522#if PY_VERSION_HEX >= 0x03050000
523 }
else if (result !=
nullptr && Py_TYPE(result)->tp_as_async !=
nullptr) {
525 if (task_cat.is_debug()) {
526 PyObject *str = PyObject_ASCII(_function);
527 PyObject *str2 = PyObject_ASCII(result);
529 << PyUnicode_AsUTF8(str) <<
" in " << *
this
530 <<
" yielded an awaitable: " << PyUnicode_AsUTF8(str2) <<
"\n";
534 if (PyObject_TypeCheck(result, &PyCoro_Type)) {
539 unaryfunc await = Py_TYPE(result)->tp_as_async->am_await;
540 _generator = await(result);
548 if (_generator !=
nullptr) {
551 PyObject *func = PyObject_GetAttrString(_generator,
"send");
552 if (func !=
nullptr) {
553 result = PyObject_CallOneArg(func, Py_None);
557 nassertr(Py_TYPE(_generator)->tp_iternext !=
nullptr, DS_interrupt);
559 result = Py_TYPE(_generator)->tp_iternext(_generator);
562 if (result ==
nullptr) {
566 Py_DECREF(_generator);
567 _generator =
nullptr;
569#if PY_VERSION_HEX >= 0x030D0000
571 if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
572 PyObject *exc = PyErr_GetRaisedException();
573 result = ((PyStopIterationObject *)exc)->value;
574 if (result ==
nullptr) {
579#elif PY_VERSION_HEX >= 0x03030000
580 if (_PyGen_FetchStopIterationValue(&result) == 0) {
582 if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
586 PyErr_Restore(
nullptr,
nullptr,
nullptr);
593 if (_function ==
nullptr) {
594 if (task_cat.is_debug()) {
596 << *
this <<
" received StopIteration from coroutine.\n";
599 Py_XDECREF(_exc_value);
603 }
else if (_function ==
nullptr) {
607 Py_XDECREF(_exception);
608 Py_XDECREF(_exc_value);
609 Py_XDECREF(_exc_traceback);
610 PyErr_Fetch(&_exception, &_exc_value, &_exc_traceback);
611 _retrieved_exception =
false;
613 if (task_cat.is_debug()) {
614 if (_exception !=
nullptr && Py_IS_TYPE(_exception, &PyType_Type)) {
616 << *
this <<
" received " << ((PyTypeObject *)_exception)->tp_name <<
" from coroutine.\n";
619 << *
this <<
" received exception from coroutine.\n";
629#if PY_VERSION_HEX >= 0x03050000
630 }
else if (result == Py_None && PyObject_TypeCheck(_generator, &PyCoro_Type)) {
636 }
else if (DtoolInstance_Check(result)) {
639 if (fut !=
nullptr) {
642 if (fut->is_task()) {
654 if (task_cat.is_debug()) {
656 << *
this <<
" is now awaiting <" << *fut <<
">.\n";
660 if (task_cat.is_debug()) {
662 << *
this <<
" would await <" << *fut <<
">, were it not already done.\n";
671 << *
this <<
" cannot await itself\n";
679 PyObject *check = PyObject_GetAttrString(result,
"_asyncio_future_blocking");
680 if (check !=
nullptr && check != Py_None) {
683 _future_done = PyObject_GetAttrString(result,
"done");
684 if (_future_done ==
nullptr || !PyCallable_Check(_future_done)) {
686 <<
"future.done is not callable\n";
689#if PY_MAJOR_VERSION >= 3
690 if (task_cat.is_debug()) {
691 PyObject *str = PyObject_ASCII(result);
693 << *
this <<
" is now polling " << PyUnicode_AsUTF8(str) <<
".done()\n";
705 if (result ==
nullptr) {
706 if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
709 if (task_cat.is_debug()) {
711 <<
"SystemExit occurred in " << *
this <<
"\n";
715 <<
"Exception occurred in " << *
this <<
"\n";
720 if (result == Py_None || _ignore_return) {
725#if PY_MAJOR_VERSION >= 3
726 if (PyLong_Check(result)) {
727 long retval = PyLong_AS_LONG(result);
729 if (PyInt_Check(result)) {
730 long retval = PyInt_AS_LONG(result);
735 Py_XDECREF(_generator);
736 _generator =
nullptr;
746 return (DoneStatus) retval;
761 PyMethodDef *meth =
nullptr;
762 if (PyCFunction_Check(result)) {
763 meth = ((PyCFunctionObject *)result)->m_ml;
764#if PY_MAJOR_VERSION >= 3
765 }
else if (Py_IS_TYPE(result, &PyMethodDescr_Type)) {
767 }
else if (strcmp(Py_TYPE(result)->tp_name,
"method_descriptor") == 0) {
769 meth = ((PyMethodDescrObject *)result)->d_method;
772 if (meth !=
nullptr && strcmp(meth->ml_name,
"done") == 0) {
777 std::ostringstream strm;
778#if PY_MAJOR_VERSION >= 3
779 PyObject *str = PyObject_ASCII(result);
780 if (str ==
nullptr) {
781 str = PyUnicode_FromString(
"<repr error>");
784 << *
this <<
" returned " << PyUnicode_AsUTF8(str);
786 PyObject *str = PyObject_Repr(result);
787 if (str ==
nullptr) {
788 str = PyString_FromString(
"<repr error>");
791 << *
this <<
" returned " << PyString_AsString(str);
795 std::string message = strm.str();
796 nassert_raise(message);
809 AsyncTask::upon_birth(manager);
828 AsyncTask::upon_death(manager, clean_exit);
831 if (_future_done !=
nullptr) {
832 Py_DECREF(_future_done);
833 _future_done =
nullptr;
836 if (_upon_death != Py_None) {
837#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
839 PyGILState_STATE gstate;
840 gstate = PyGILState_Ensure();
843 call_function(_upon_death);
845#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
846 PyGILState_Release(gstate);
849 unregister_from_owner();
857 if (_owner != Py_None && !_registered_to_owner) {
858#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
860 PyGILState_STATE gstate;
861 gstate = PyGILState_Ensure();
864 _registered_to_owner =
true;
865 call_owner_method(
"_addTask");
867#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
868 PyGILState_Release(gstate);
877unregister_from_owner() {
879 if (_owner != Py_None && _registered_to_owner) {
880#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
882 PyGILState_STATE gstate;
883 gstate = PyGILState_Ensure();
886 _registered_to_owner =
false;
887 call_owner_method(
"_clearTask");
889#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
890 PyGILState_Release(gstate);
900call_owner_method(
const char *method_name) {
901 if (_owner != Py_None) {
902 PyObject *func = PyObject_GetAttrString(_owner, (
char *)method_name);
903 if (func ==
nullptr) {
905 <<
"Owner object added to " << *
this <<
" has no method "
906 << method_name <<
"().\n";
920call_function(PyObject *function) {
921 if (function != Py_None) {
924 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.