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 >= 0x03030000
549 if (_PyGen_FetchStopIterationValue(&result) == 0) {
551 if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
555 PyErr_Restore(
nullptr,
nullptr,
nullptr);
562 if (_function ==
nullptr) {
563 if (task_cat.is_debug()) {
565 << *
this <<
" received StopIteration from coroutine.\n";
568 Py_XDECREF(_exc_value);
572 }
else if (_function ==
nullptr) {
576 Py_XDECREF(_exception);
577 Py_XDECREF(_exc_value);
578 Py_XDECREF(_exc_traceback);
579 PyErr_Fetch(&_exception, &_exc_value, &_exc_traceback);
580 _retrieved_exception =
false;
582 if (task_cat.is_debug()) {
583 if (_exception !=
nullptr && Py_TYPE(_exception) == &PyType_Type) {
585 << *
this <<
" received " << ((PyTypeObject *)_exception)->tp_name <<
" from coroutine.\n";
588 << *
this <<
" received exception from coroutine.\n";
598#if PY_VERSION_HEX >= 0x03050000
599 }
else if (result == Py_None && PyCoro_CheckExact(_generator)) {
605 }
else if (DtoolInstance_Check(result)) {
608 if (fut !=
nullptr) {
611 if (fut->is_task()) {
623 if (task_cat.is_debug()) {
625 << *
this <<
" is now awaiting <" << *fut <<
">.\n";
629 if (task_cat.is_debug()) {
631 << *
this <<
" would await <" << *fut <<
">, were it not already done.\n";
640 << *
this <<
" cannot await itself\n";
648 PyObject *check = PyObject_GetAttrString(result,
"_asyncio_future_blocking");
649 if (check !=
nullptr && check != Py_None) {
652 _future_done = PyObject_GetAttrString(result,
"done");
653 if (_future_done ==
nullptr || !PyCallable_Check(_future_done)) {
655 <<
"future.done is not callable\n";
658#if PY_MAJOR_VERSION >= 3
659 if (task_cat.is_debug()) {
660 PyObject *str = PyObject_ASCII(result);
662 << *
this <<
" is now polling " << PyUnicode_AsUTF8(str) <<
".done()\n";
674 if (result ==
nullptr) {
675 if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
678 if (task_cat.is_debug()) {
680 <<
"SystemExit occurred in " << *
this <<
"\n";
684 <<
"Exception occurred in " << *
this <<
"\n";
689 if (result == Py_None || _ignore_return) {
694#if PY_MAJOR_VERSION >= 3
695 if (PyLong_Check(result)) {
696 long retval = PyLong_AS_LONG(result);
698 if (PyInt_Check(result)) {
699 long retval = PyInt_AS_LONG(result);
704 Py_XDECREF(_generator);
705 _generator =
nullptr;
715 return (DoneStatus) retval;
730 PyMethodDef *meth =
nullptr;
731 if (PyCFunction_Check(result)) {
732 meth = ((PyCFunctionObject *)result)->m_ml;
733#if PY_MAJOR_VERSION >= 3
734 }
else if (Py_TYPE(result) == &PyMethodDescr_Type) {
736 }
else if (strcmp(Py_TYPE(result)->tp_name,
"method_descriptor") == 0) {
738 meth = ((PyMethodDescrObject *)result)->d_method;
741 if (meth !=
nullptr && strcmp(meth->ml_name,
"done") == 0) {
746 std::ostringstream strm;
747#if PY_MAJOR_VERSION >= 3
748 PyObject *str = PyObject_ASCII(result);
749 if (str ==
nullptr) {
750 str = PyUnicode_FromString(
"<repr error>");
753 << *
this <<
" returned " << PyUnicode_AsUTF8(str);
755 PyObject *str = PyObject_Repr(result);
756 if (str ==
nullptr) {
757 str = PyString_FromString(
"<repr error>");
760 << *
this <<
" returned " << PyString_AsString(str);
764 std::string message = strm.str();
765 nassert_raise(message);
778 AsyncTask::upon_birth(manager);
797 AsyncTask::upon_death(manager, clean_exit);
800 if (_future_done !=
nullptr) {
801 Py_DECREF(_future_done);
802 _future_done =
nullptr;
805 if (_upon_death != Py_None) {
806#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
808 PyGILState_STATE gstate;
809 gstate = PyGILState_Ensure();
812 call_function(_upon_death);
814#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
815 PyGILState_Release(gstate);
818 unregister_from_owner();
826 if (_owner != Py_None && !_registered_to_owner) {
827#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
829 PyGILState_STATE gstate;
830 gstate = PyGILState_Ensure();
833 _registered_to_owner =
true;
834 call_owner_method(
"_addTask");
836#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
837 PyGILState_Release(gstate);
846unregister_from_owner() {
848 if (_owner != Py_None && _registered_to_owner) {
849#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
851 PyGILState_STATE gstate;
852 gstate = PyGILState_Ensure();
855 _registered_to_owner =
false;
856 call_owner_method(
"_clearTask");
858#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
859 PyGILState_Release(gstate);
869call_owner_method(
const char *method_name) {
870 if (_owner != Py_None) {
871 PyObject *func = PyObject_GetAttrString(_owner, (
char *)method_name);
872 if (func ==
nullptr) {
874 <<
"Owner object added to " << *
this <<
" has no method "
875 << method_name <<
"().\n";
889call_function(PyObject *function) {
890 if (function != Py_None) {
893 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.