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 #ifndef SIMPLE_THREADS 77 #ifdef WITH_THREAD // This symbol defined within Python.h 91 if (_exception !=
nullptr && !_retrieved_exception) {
93 << *
this <<
" exception was never retrieved:\n";
94 PyErr_Restore(_exception, _exc_value, _exc_traceback);
96 PyErr_Restore(
nullptr,
nullptr,
nullptr);
99 _exc_traceback =
nullptr;
103 Py_XDECREF(_function);
106 Py_XDECREF(_exception);
107 Py_XDECREF(_exc_value);
108 Py_XDECREF(_exc_traceback);
109 Py_XDECREF(_generator);
111 Py_XDECREF(_upon_death);
119 set_function(PyObject *
function) {
120 Py_XDECREF(_function);
122 _function =
function;
123 Py_INCREF(_function);
124 if (_function != Py_None && !PyCallable_Check(_function)) {
125 nassert_raise(
"Invalid function passed to PythonTask");
135 set_args(PyObject *args,
bool append_task) {
139 if (args == Py_None) {
141 _args = PyTuple_New(0);
143 if (PySequence_Check(args)) {
144 _args = PySequence_Tuple(args);
148 if (_args ==
nullptr) {
149 nassert_raise(
"Invalid args passed to PythonTask");
150 _args = PyTuple_New(0);
153 _append_task = append_task;
159 PyObject *PythonTask::
167 int num_args = PyTuple_GET_SIZE(_args);
168 PyObject *with_task = PyTuple_New(num_args + 1);
169 for (
int i = 0; i < num_args; ++i) {
170 PyObject *item = PyTuple_GET_ITEM(_args, i);
172 PyTuple_SET_ITEM(with_task, i, item);
177 PyTuple_SET_ITEM(with_task, num_args,
self);
191 set_upon_death(PyObject *upon_death) {
192 Py_XDECREF(_upon_death);
194 _upon_death = upon_death;
195 Py_INCREF(_upon_death);
196 if (_upon_death != Py_None && !PyCallable_Check(_upon_death)) {
197 nassert_raise(
"Invalid upon_death function passed to PythonTask");
210 set_owner(PyObject *owner) {
212 if (owner != Py_None) {
213 PyObject *add = PyObject_GetAttrString(owner,
"_addTask");
214 PyObject *clear = PyObject_GetAttrString(owner,
"_clearTask");
216 if (add ==
nullptr || !PyCallable_Check(add) ||
217 clear ==
nullptr || !PyCallable_Check(clear)) {
218 Dtool_Raise_TypeError(
"owner object should have _addTask and _clearTask methods");
224 if (_owner !=
nullptr && _owner != Py_None && _state != S_inactive) {
225 unregister_from_owner();
232 if (_owner != Py_None && _state != S_inactive) {
242 PyObject *PythonTask::
244 nassertr(done(),
nullptr);
246 if (_exception ==
nullptr) {
248 Py_XINCREF(_exc_value);
251 _retrieved_exception =
true;
252 Py_INCREF(_exception);
253 Py_XINCREF(_exc_value);
254 Py_XINCREF(_exc_traceback);
255 PyErr_Restore(_exception, _exc_value, _exc_traceback);
285 __setattr__(PyObject *
self, PyObject *attr, PyObject *v) {
286 if (PyObject_GenericSetAttr(
self, attr, v) == 0) {
290 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
296 if (task_cat.is_debug()) {
297 PyObject *str = PyObject_Repr(v);
299 << *
this <<
": task." 300 #if PY_MAJOR_VERSION >= 3 301 << PyUnicode_AsUTF8(attr) <<
" = " 302 << PyUnicode_AsUTF8(str) <<
"\n";
304 << PyString_AsString(attr) <<
" = " 305 << PyString_AsString(str) <<
"\n";
310 return PyDict_SetItem(__dict__, attr, v);
320 __delattr__(PyObject *
self, PyObject *attr) {
321 if (PyObject_GenericSetAttr(
self, attr,
nullptr) == 0) {
325 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
331 if (PyDict_DelItem(__dict__, attr) == -1) {
333 #if PY_MAJOR_VERSION < 3 334 PyErr_Format(PyExc_AttributeError,
335 "'PythonTask' object has no attribute '%.400s'",
336 PyString_AS_STRING(attr));
338 PyErr_Format(PyExc_AttributeError,
339 "'PythonTask' object has no attribute '%U'",
354 PyObject *PythonTask::
355 __getattr__(PyObject *attr)
const {
361 PyObject *item = PyDict_GetItem(__dict__, attr);
363 if (item ==
nullptr) {
365 #if PY_MAJOR_VERSION < 3 366 PyErr_Format(PyExc_AttributeError,
367 "'PythonTask' object has no attribute '%.400s'",
368 PyString_AS_STRING(attr));
370 PyErr_Format(PyExc_AttributeError,
371 "'PythonTask' object has no attribute '%U'",
386 __traverse__(visitproc visit,
void *arg) {
423 return _function != Py_None;
431 AsyncTask::DoneStatus PythonTask::
433 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 435 PyGILState_STATE gstate;
436 gstate = PyGILState_Ensure();
439 DoneStatus result = do_python_task();
441 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 442 PyGILState_Release(gstate);
452 AsyncTask::DoneStatus PythonTask::
454 PyObject *result =
nullptr;
457 if (_future_done !=
nullptr) {
458 PyObject *is_done = PyObject_CallObject(_future_done,
nullptr);
459 if (!PyObject_IsTrue(is_done)) {
465 Py_DECREF(_future_done);
466 _future_done =
nullptr;
469 if (_generator ==
nullptr) {
471 nassertr(_function !=
nullptr, DS_interrupt);
473 PyObject *args = get_args();
474 result = PythonThread::call_python_func(_function, args);
477 if (result !=
nullptr && PyGen_Check(result)) {
480 if (task_cat.is_debug()) {
481 #if PY_MAJOR_VERSION >= 3 482 PyObject *str = PyObject_ASCII(_function);
484 << PyUnicode_AsUTF8(str) <<
" in " << *
this 485 <<
" yielded a generator.\n";
487 PyObject *str = PyObject_Repr(_function);
489 << PyString_AsString(str) <<
" in " << *
this 490 <<
" yielded a generator.\n";
497 #if PY_VERSION_HEX >= 0x03050000 498 }
else if (result !=
nullptr && Py_TYPE(result)->tp_as_async !=
nullptr) {
500 if (task_cat.is_debug()) {
501 PyObject *str = PyObject_ASCII(_function);
502 PyObject *str2 = PyObject_ASCII(result);
504 << PyUnicode_AsUTF8(str) <<
" in " << *
this 505 <<
" yielded an awaitable: " << PyUnicode_AsUTF8(str2) <<
"\n";
509 if (PyCoro_CheckExact(result)) {
514 unaryfunc await = Py_TYPE(result)->tp_as_async->am_await;
515 _generator = await(result);
523 if (_generator !=
nullptr) {
526 PyObject *func = PyObject_GetAttrString(_generator,
"send");
527 nassertr(func !=
nullptr, DS_interrupt);
528 result = PyObject_CallFunctionObjArgs(func, Py_None,
nullptr);
531 if (result ==
nullptr) {
535 Py_DECREF(_generator);
536 _generator =
nullptr;
538 #if PY_VERSION_HEX >= 0x03030000 539 if (_PyGen_FetchStopIterationValue(&result) == 0) {
541 if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
545 PyErr_Restore(
nullptr,
nullptr,
nullptr);
552 if (_function ==
nullptr) {
553 if (task_cat.is_debug()) {
555 << *
this <<
" received StopIteration from coroutine.\n";
558 Py_XDECREF(_exc_value);
562 }
else if (_function ==
nullptr) {
566 Py_XDECREF(_exception);
567 Py_XDECREF(_exc_value);
568 Py_XDECREF(_exc_traceback);
569 PyErr_Fetch(&_exception, &_exc_value, &_exc_traceback);
570 _retrieved_exception =
false;
572 if (task_cat.is_debug()) {
573 if (_exception !=
nullptr && Py_TYPE(_exception) == &PyType_Type) {
575 << *
this <<
" received " << ((PyTypeObject *)_exception)->tp_name <<
" from coroutine.\n";
578 << *
this <<
" received exception from coroutine.\n";
588 }
else if (DtoolInstance_Check(result)) {
591 if (fut !=
nullptr) {
594 if (fut->is_task()) {
606 if (task_cat.is_debug()) {
608 << *
this <<
" is now awaiting <" << *fut <<
">.\n";
612 if (task_cat.is_debug()) {
614 << *
this <<
" would await <" << *fut <<
">, were it not already done.\n";
623 << *
this <<
" cannot await itself\n";
631 PyObject *check = PyObject_GetAttrString(result,
"_asyncio_future_blocking");
632 if (check !=
nullptr && check != Py_None) {
635 _future_done = PyObject_GetAttrString(result,
"done");
636 if (_future_done ==
nullptr || !PyCallable_Check(_future_done)) {
638 <<
"future.done is not callable\n";
641 #if PY_MAJOR_VERSION >= 3 642 if (task_cat.is_debug()) {
643 PyObject *str = PyObject_ASCII(result);
645 << *
this <<
" is now polling " << PyUnicode_AsUTF8(str) <<
".done()\n";
657 if (result ==
nullptr) {
658 if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
661 if (task_cat.is_debug()) {
663 <<
"SystemExit occurred in " << *
this <<
"\n";
667 <<
"Exception occurred in " << *
this <<
"\n";
672 if (result == Py_None || _ignore_return) {
677 #if PY_MAJOR_VERSION >= 3 678 if (PyLong_Check(result)) {
679 long retval = PyLong_AS_LONG(result);
681 if (PyInt_Check(result)) {
682 long retval = PyInt_AS_LONG(result);
687 Py_XDECREF(_generator);
688 _generator =
nullptr;
698 return (DoneStatus) retval;
713 PyMethodDef *meth =
nullptr;
714 if (PyCFunction_Check(result)) {
715 meth = ((PyCFunctionObject *)result)->m_ml;
716 #if PY_MAJOR_VERSION >= 3 717 }
else if (Py_TYPE(result) == &PyMethodDescr_Type) {
719 }
else if (strcmp(Py_TYPE(result)->tp_name,
"method_descriptor") == 0) {
721 meth = ((PyMethodDescrObject *)result)->d_method;
724 if (meth !=
nullptr && strcmp(meth->ml_name,
"done") == 0) {
729 std::ostringstream strm;
730 #if PY_MAJOR_VERSION >= 3 731 PyObject *str = PyObject_ASCII(result);
732 if (str ==
nullptr) {
733 str = PyUnicode_FromString(
"<repr error>");
736 << *
this <<
" returned " << PyUnicode_AsUTF8(str);
738 PyObject *str = PyObject_Repr(result);
739 if (str ==
nullptr) {
740 str = PyString_FromString(
"<repr error>");
743 << *
this <<
" returned " << PyString_AsString(str);
747 std::string message = strm.str();
748 nassert_raise(message);
761 AsyncTask::upon_birth(manager);
780 AsyncTask::upon_death(manager, clean_exit);
782 if (_upon_death != Py_None) {
783 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 785 PyGILState_STATE gstate;
786 gstate = PyGILState_Ensure();
789 call_function(_upon_death);
791 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 792 PyGILState_Release(gstate);
795 unregister_from_owner();
802 register_to_owner() {
803 if (_owner != Py_None && !_registered_to_owner) {
804 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 806 PyGILState_STATE gstate;
807 gstate = PyGILState_Ensure();
810 _registered_to_owner =
true;
811 call_owner_method(
"_addTask");
813 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 814 PyGILState_Release(gstate);
823 unregister_from_owner() {
825 if (_owner != Py_None && _registered_to_owner) {
826 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 828 PyGILState_STATE gstate;
829 gstate = PyGILState_Ensure();
832 _registered_to_owner =
false;
833 call_owner_method(
"_clearTask");
835 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 836 PyGILState_Release(gstate);
846 call_owner_method(
const char *method_name) {
847 if (_owner != Py_None) {
848 PyObject *func = PyObject_GetAttrString(_owner, (
char *)method_name);
849 if (func ==
nullptr) {
851 <<
"Owner object added to " << *
this <<
" has no method " 852 << method_name <<
"().\n";
866 call_function(PyObject *
function) {
867 if (
function != Py_None) {
870 PyObject *result = PyObject_CallFunctionObjArgs(
function,
self,
nullptr);
876 #endif // HAVE_PYTHON PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class represents a thread-safe handle to a promised future result of an asynchronous operation,...
A class to manage a loose queue of isolated tasks, which can be performed either synchronously (in th...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
is_alive
Returns true if the task is currently active or sleeping on some task chain, meaning that it will be ...
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.
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().
This class represents a concrete task performed by an AsyncManager.
TypeHandle is the identifier used to differentiate C++ class types.