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()) {
604 if (task_cat.is_debug()) {
606 << *
this <<
" is now awaiting <" << *fut <<
">.\n";
610 if (task_cat.is_debug()) {
612 << *
this <<
" would await <" << *fut <<
">, were it not already done.\n";
621 << *
this <<
" cannot await itself\n";
629 PyObject *check = PyObject_GetAttrString(result,
"_asyncio_future_blocking");
630 if (check !=
nullptr && check != Py_None) {
633 _future_done = PyObject_GetAttrString(result,
"done");
634 if (_future_done ==
nullptr || !PyCallable_Check(_future_done)) {
636 <<
"future.done is not callable\n";
639 #if PY_MAJOR_VERSION >= 3 640 if (task_cat.is_debug()) {
641 PyObject *str = PyObject_ASCII(result);
643 << *
this <<
" is now polling " << PyUnicode_AsUTF8(str) <<
".done()\n";
655 if (result ==
nullptr) {
656 if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
659 if (task_cat.is_debug()) {
661 <<
"SystemExit occurred in " << *
this <<
"\n";
665 <<
"Exception occurred in " << *
this <<
"\n";
670 if (result == Py_None || _ignore_return) {
675 #if PY_MAJOR_VERSION >= 3 676 if (PyLong_Check(result)) {
677 long retval = PyLong_AS_LONG(result);
679 if (PyInt_Check(result)) {
680 long retval = PyInt_AS_LONG(result);
685 Py_XDECREF(_generator);
686 _generator =
nullptr;
696 return (DoneStatus) retval;
711 PyMethodDef *meth =
nullptr;
712 if (PyCFunction_Check(result)) {
713 meth = ((PyCFunctionObject *)result)->m_ml;
714 #if PY_MAJOR_VERSION >= 3 715 }
else if (Py_TYPE(result) == &PyMethodDescr_Type) {
717 }
else if (strcmp(Py_TYPE(result)->tp_name,
"method_descriptor") == 0) {
719 meth = ((PyMethodDescrObject *)result)->d_method;
722 if (meth !=
nullptr && strcmp(meth->ml_name,
"done") == 0) {
727 std::ostringstream strm;
728 #if PY_MAJOR_VERSION >= 3 729 PyObject *str = PyObject_ASCII(result);
730 if (str ==
nullptr) {
731 str = PyUnicode_FromString(
"<repr error>");
734 << *
this <<
" returned " << PyUnicode_AsUTF8(str);
736 PyObject *str = PyObject_Repr(result);
737 if (str ==
nullptr) {
738 str = PyString_FromString(
"<repr error>");
741 << *
this <<
" returned " << PyString_AsString(str);
745 std::string message = strm.str();
746 nassert_raise(message);
759 AsyncTask::upon_birth(manager);
778 AsyncTask::upon_death(manager, clean_exit);
780 if (_upon_death != Py_None) {
781 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 783 PyGILState_STATE gstate;
784 gstate = PyGILState_Ensure();
787 call_function(_upon_death);
789 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 790 PyGILState_Release(gstate);
793 unregister_from_owner();
800 register_to_owner() {
801 if (_owner != Py_None && !_registered_to_owner) {
802 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 804 PyGILState_STATE gstate;
805 gstate = PyGILState_Ensure();
808 _registered_to_owner =
true;
809 call_owner_method(
"_addTask");
811 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 812 PyGILState_Release(gstate);
821 unregister_from_owner() {
823 if (_owner != Py_None && _registered_to_owner) {
824 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 826 PyGILState_STATE gstate;
827 gstate = PyGILState_Ensure();
830 _registered_to_owner =
false;
831 call_owner_method(
"_clearTask");
833 #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) 834 PyGILState_Release(gstate);
844 call_owner_method(
const char *method_name) {
845 if (_owner != Py_None) {
846 PyObject *func = PyObject_GetAttrString(_owner, (
char *)method_name);
847 if (func ==
nullptr) {
849 <<
"Owner object added to " << *
this <<
" has no method " 850 << method_name <<
"().\n";
864 call_function(PyObject *
function) {
865 if (
function != Py_None) {
868 PyObject *result = PyObject_CallFunctionObjArgs(
function,
self,
nullptr);
874 #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.
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.