pythonTask.cxx 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. /**
  2. * PANDA 3D SOFTWARE
  3. * Copyright (c) Carnegie Mellon University. All rights reserved.
  4. *
  5. * All use of this software is subject to the terms of the revised BSD
  6. * license. You should have received a copy of this license along
  7. * with this source code in a file named "LICENSE."
  8. *
  9. * @file pythonTask.cxx
  10. * @author drose
  11. * @date 2008-09-16
  12. */
  13. #include "pythonTask.h"
  14. #include "pnotify.h"
  15. #include "config_event.h"
  16. #ifdef HAVE_PYTHON
  17. #include "py_panda.h"
  18. #include "pythonThread.h"
  19. #include "asyncTaskManager.h"
  20. #include "asyncFuture_ext.h"
  21. TypeHandle PythonTask::_type_handle;
  22. #ifndef CPPPARSER
  23. extern struct Dtool_PyTypedObject Dtool_TypedReferenceCount;
  24. extern struct Dtool_PyTypedObject Dtool_AsyncFuture;
  25. extern struct Dtool_PyTypedObject Dtool_PythonTask;
  26. #endif
  27. /**
  28. *
  29. */
  30. PythonTask::
  31. PythonTask(PyObject *func_or_coro, const std::string &name) :
  32. AsyncTask(name),
  33. _function(nullptr),
  34. _args(nullptr),
  35. _upon_death(nullptr),
  36. _owner(nullptr),
  37. _exception(nullptr),
  38. _exc_value(nullptr),
  39. _exc_traceback(nullptr),
  40. _generator(nullptr),
  41. _fut_waiter(nullptr),
  42. _ignore_return(false),
  43. _registered_to_owner(false),
  44. _retrieved_exception(false) {
  45. nassertv(func_or_coro != nullptr);
  46. if (func_or_coro == Py_None || PyCallable_Check(func_or_coro)) {
  47. _function = func_or_coro;
  48. Py_INCREF(_function);
  49. } else if (PyCoro_CheckExact(func_or_coro)) {
  50. // We also allow passing in a coroutine, because why not.
  51. _generator = func_or_coro;
  52. Py_INCREF(_generator);
  53. } else if (PyGen_CheckExact(func_or_coro)) {
  54. // Something emulating a coroutine.
  55. _generator = func_or_coro;
  56. Py_INCREF(_generator);
  57. } else {
  58. nassert_raise("Invalid function passed to PythonTask");
  59. }
  60. set_args(Py_None, true);
  61. set_upon_death(Py_None);
  62. set_owner(Py_None);
  63. __dict__ = PyDict_New();
  64. #if !defined(SIMPLE_THREADS) && defined(WITH_THREAD) && PY_VERSION_HEX < 0x03090000
  65. // Ensure that the Python threading system is initialized and ready to go.
  66. // WITH_THREAD symbol defined within Python.h
  67. // PyEval_InitThreads is now a deprecated no-op in Python 3.9+
  68. PyEval_InitThreads();
  69. #endif
  70. }
  71. /**
  72. *
  73. */
  74. PythonTask::
  75. ~PythonTask() {
  76. // If the coroutine threw an exception, and there was no opportunity to
  77. // handle it, let the user know.
  78. if (_exception != nullptr && !_retrieved_exception) {
  79. task_cat.error()
  80. << *this << " exception was never retrieved:\n";
  81. PyErr_Restore(_exception, _exc_value, _exc_traceback);
  82. PyErr_Print();
  83. PyErr_Restore(nullptr, nullptr, nullptr);
  84. _exception = nullptr;
  85. _exc_value = nullptr;
  86. _exc_traceback = nullptr;
  87. }
  88. Py_XDECREF(_function);
  89. Py_DECREF(_args);
  90. Py_DECREF(__dict__);
  91. Py_XDECREF(_exception);
  92. Py_XDECREF(_exc_value);
  93. Py_XDECREF(_exc_traceback);
  94. Py_XDECREF(_generator);
  95. Py_XDECREF(_owner);
  96. Py_XDECREF(_upon_death);
  97. }
  98. /**
  99. * Replaces the function that is called when the task runs. The parameter
  100. * should be a Python callable object.
  101. */
  102. void PythonTask::
  103. set_function(PyObject *function) {
  104. Py_XDECREF(_function);
  105. _function = function;
  106. Py_INCREF(_function);
  107. if (_function != Py_None && !PyCallable_Check(_function)) {
  108. nassert_raise("Invalid function passed to PythonTask");
  109. }
  110. }
  111. /**
  112. * Replaces the argument list that is passed to the task function. The
  113. * parameter should be a tuple or list of arguments, or None to indicate the
  114. * empty list.
  115. */
  116. void PythonTask::
  117. set_args(PyObject *args, bool append_task) {
  118. Py_XDECREF(_args);
  119. _args = nullptr;
  120. if (args == Py_None) {
  121. // None means no arguments; create an empty tuple.
  122. _args = PyTuple_New(0);
  123. } else {
  124. if (PySequence_Check(args)) {
  125. _args = PySequence_Tuple(args);
  126. }
  127. }
  128. if (_args == nullptr) {
  129. nassert_raise("Invalid args passed to PythonTask");
  130. _args = PyTuple_New(0);
  131. }
  132. _append_task = append_task;
  133. }
  134. /**
  135. * Returns the argument list that is passed to the task function.
  136. */
  137. PyObject *PythonTask::
  138. get_args() {
  139. if (_append_task) {
  140. // If we want to append the task, we have to create a new tuple with space
  141. // for one more at the end. We have to do this dynamically each time, to
  142. // avoid storing the task itself in its own arguments list, and thereby
  143. // creating a cyclical reference.
  144. int num_args = PyTuple_GET_SIZE(_args);
  145. PyObject *with_task = PyTuple_New(num_args + 1);
  146. for (int i = 0; i < num_args; ++i) {
  147. PyObject *item = PyTuple_GET_ITEM(_args, i);
  148. Py_INCREF(item);
  149. PyTuple_SET_ITEM(with_task, i, item);
  150. }
  151. this->ref();
  152. PyObject *self = DTool_CreatePyInstance(this, Dtool_PythonTask, true, false);
  153. PyTuple_SET_ITEM(with_task, num_args, self);
  154. return with_task;
  155. } else {
  156. Py_INCREF(_args);
  157. return _args;
  158. }
  159. }
  160. /**
  161. * Replaces the function that is called when the task finishes. The parameter
  162. * should be a Python callable object.
  163. */
  164. void PythonTask::
  165. set_upon_death(PyObject *upon_death) {
  166. Py_XDECREF(_upon_death);
  167. _upon_death = upon_death;
  168. Py_INCREF(_upon_death);
  169. if (_upon_death != Py_None && !PyCallable_Check(_upon_death)) {
  170. nassert_raise("Invalid upon_death function passed to PythonTask");
  171. }
  172. }
  173. /**
  174. * Specifies a Python object that serves as the "owner" for the task. This
  175. * owner object must have two methods: _addTask() and _clearTask(), which will
  176. * be called with one parameter, the task object.
  177. *
  178. * owner._addTask() is called when the task is added into the active task
  179. * list, and owner._clearTask() is called when it is removed.
  180. */
  181. void PythonTask::
  182. set_owner(PyObject *owner) {
  183. #ifndef NDEBUG
  184. if (owner != Py_None) {
  185. PyObject *add = PyObject_GetAttrString(owner, "_addTask");
  186. PyErr_Clear();
  187. PyObject *clear = PyObject_GetAttrString(owner, "_clearTask");
  188. PyErr_Clear();
  189. bool valid_add = false;
  190. if (add != nullptr) {
  191. valid_add = PyCallable_Check(add);
  192. Py_DECREF(add);
  193. }
  194. bool valid_clear = false;
  195. if (clear != nullptr) {
  196. valid_clear = PyCallable_Check(clear);
  197. Py_DECREF(clear);
  198. }
  199. if (!valid_add || !valid_clear) {
  200. Dtool_Raise_TypeError("owner object should have _addTask and _clearTask methods");
  201. return;
  202. }
  203. }
  204. #endif
  205. if (_owner != nullptr && _owner != Py_None && _state != S_inactive) {
  206. unregister_from_owner();
  207. }
  208. Py_XDECREF(_owner);
  209. _owner = owner;
  210. Py_INCREF(_owner);
  211. if (_owner != Py_None && _state != S_inactive) {
  212. register_to_owner();
  213. }
  214. }
  215. /**
  216. * Returns the result of this task's execution, as set by set_result() within
  217. * the task or returned from a coroutine added to the task manager. If an
  218. * exception occurred within this task, it is raised instead.
  219. */
  220. PyObject *PythonTask::
  221. get_result() const {
  222. nassertr(done(), nullptr);
  223. if (_exception == nullptr) {
  224. // The result of the call is stored in _exc_value.
  225. Py_XINCREF(_exc_value);
  226. return _exc_value;
  227. } else {
  228. _retrieved_exception = true;
  229. Py_INCREF(_exception);
  230. Py_XINCREF(_exc_value);
  231. Py_XINCREF(_exc_traceback);
  232. PyErr_Restore(_exception, _exc_value, _exc_traceback);
  233. return nullptr;
  234. }
  235. }
  236. /**
  237. * If an exception occurred during execution of this task, returns it. This
  238. * is only set if this task returned a coroutine or generator.
  239. */
  240. /*PyObject *PythonTask::
  241. exception() const {
  242. if (_exception == nullptr) {
  243. Py_INCREF(Py_None);
  244. return Py_None;
  245. } else if (_exc_value == nullptr || _exc_value == Py_None) {
  246. return PyObject_CallNoArgs(_exception);
  247. } else if (PyTuple_Check(_exc_value)) {
  248. return PyObject_Call(_exception, _exc_value, nullptr);
  249. } else {
  250. return PyObject_CallOneArg(_exception, _exc_value);
  251. }
  252. }*/
  253. /**
  254. * Maps from an expression like "task.attr_name = v". This is customized here
  255. * so we can support some traditional task interfaces that supported directly
  256. * assigning certain values. We also support adding arbitrary data to the
  257. * Task object.
  258. */
  259. int PythonTask::
  260. __setattr__(PyObject *self, PyObject *attr, PyObject *v) {
  261. if (!PyUnicode_Check(attr)) {
  262. PyErr_Format(PyExc_TypeError,
  263. "attribute name must be string, not '%.200s'",
  264. attr->ob_type->tp_name);
  265. return -1;
  266. }
  267. PyObject *descr = _PyType_Lookup(Py_TYPE(self), attr);
  268. if (descr != nullptr) {
  269. Py_INCREF(descr);
  270. descrsetfunc f = descr->ob_type->tp_descr_set;
  271. if (f != nullptr) {
  272. return f(descr, self, v);
  273. }
  274. }
  275. if (task_cat.is_debug()) {
  276. PyObject *str = PyObject_Repr(v);
  277. task_cat.debug()
  278. << *this << ": task."
  279. << PyUnicode_AsUTF8(attr) << " = "
  280. << PyUnicode_AsUTF8(str) << "\n";
  281. Py_DECREF(str);
  282. }
  283. return PyDict_SetItem(__dict__, attr, v);
  284. }
  285. /**
  286. * Maps from an expression like "del task.attr_name". This is customized here
  287. * so we can support some traditional task interfaces that supported directly
  288. * assigning certain values. We also support adding arbitrary data to the
  289. * Task object.
  290. */
  291. int PythonTask::
  292. __delattr__(PyObject *self, PyObject *attr) {
  293. if (PyObject_GenericSetAttr(self, attr, nullptr) == 0) {
  294. return 0;
  295. }
  296. if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
  297. return -1;
  298. }
  299. PyErr_Clear();
  300. if (PyDict_DelItem(__dict__, attr) == -1) {
  301. // PyDict_DelItem does not raise an exception.
  302. PyErr_Format(PyExc_AttributeError,
  303. "'PythonTask' object has no attribute '%U'",
  304. attr);
  305. return -1;
  306. }
  307. return 0;
  308. }
  309. /**
  310. * Maps from an expression like "task.attr_name". This is customized here so
  311. * we can support some traditional task interfaces that supported directly
  312. * querying certain values. We also support adding arbitrary data to the Task
  313. * object.
  314. */
  315. PyObject *PythonTask::
  316. __getattribute__(PyObject *self, PyObject *attr) const {
  317. // We consult the instance dict first, since the user may have overridden a
  318. // method or something.
  319. PyObject *item = PyDict_GetItem(__dict__, attr);
  320. if (item != nullptr) {
  321. // PyDict_GetItem returns a borrowed reference.
  322. Py_INCREF(item);
  323. return item;
  324. }
  325. return PyObject_GenericGetAttr(self, attr);
  326. }
  327. /**
  328. * Called by Python to implement cycle detection.
  329. */
  330. int PythonTask::
  331. __traverse__(visitproc visit, void *arg) {
  332. /*
  333. Py_VISIT(_function);
  334. Py_VISIT(_args);
  335. Py_VISIT(_upon_death);
  336. Py_VISIT(_owner);
  337. Py_VISIT(__dict__);
  338. Py_VISIT(_generator);
  339. */
  340. return 0;
  341. }
  342. /**
  343. * Called by Python to implement cycle breaking.
  344. */
  345. int PythonTask::
  346. __clear__() {
  347. /*
  348. Py_CLEAR(_function);
  349. Py_CLEAR(_args);
  350. Py_CLEAR(_upon_death);
  351. Py_CLEAR(_owner);
  352. Py_CLEAR(__dict__);
  353. Py_CLEAR(_generator);
  354. */
  355. return 0;
  356. }
  357. /**
  358. * Cancels this task. This is equivalent to remove(), except for coroutines,
  359. * for which it will throw an exception into any currently pending await.
  360. */
  361. bool PythonTask::
  362. cancel() {
  363. AsyncTaskManager *manager = _manager;
  364. if (manager != nullptr) {
  365. nassertr(_chain->_manager == manager, false);
  366. if (task_cat.is_debug()) {
  367. task_cat.debug()
  368. << "Cancelling " << *this << "\n";
  369. }
  370. bool must_cancel = true;
  371. if (_fut_waiter != nullptr) {
  372. // Cancel the future that this task is waiting on. Note that we do this
  373. // before grabbing the lock, since this operation may also grab it. This
  374. // means that _fut_waiter is only protected by the GIL.
  375. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  376. // Use PyGILState to protect this asynchronous call.
  377. PyGILState_STATE gstate;
  378. gstate = PyGILState_Ensure();
  379. #endif
  380. // Shortcut for unextended AsyncFuture.
  381. if (Py_TYPE(_fut_waiter) == (PyTypeObject *)&Dtool_AsyncFuture) {
  382. AsyncFuture *fut = (AsyncFuture *)DtoolInstance_VOID_PTR(_fut_waiter);
  383. if (!fut->done()) {
  384. fut->cancel();
  385. }
  386. if (fut->done()) {
  387. // We don't need this anymore.
  388. Py_DECREF(_fut_waiter);
  389. _fut_waiter = nullptr;
  390. }
  391. }
  392. else {
  393. PyObject *result = PyObject_CallMethod(_fut_waiter, "cancel", nullptr);
  394. Py_XDECREF(result);
  395. }
  396. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  397. PyGILState_Release(gstate);
  398. #endif
  399. // Keep _fut_waiter in any case, because we may need to cancel it again
  400. // later if it ignores the cancellation.
  401. }
  402. MutexHolder holder(manager->_lock);
  403. if (_state == S_awaiting) {
  404. // Reactivate it so that it can receive a CancelledException.
  405. if (must_cancel) {
  406. _must_cancel = true;
  407. }
  408. _state = AsyncTask::S_active;
  409. _chain->_active.push_back(this);
  410. --_chain->_num_awaiting_tasks;
  411. return true;
  412. }
  413. else if (must_cancel || _fut_waiter != nullptr) {
  414. // We may be polling an external future, so we still need to throw a
  415. // CancelledException and allow it to be caught.
  416. if (must_cancel) {
  417. _must_cancel = true;
  418. }
  419. return true;
  420. }
  421. else if (_chain->do_remove(this, true)) {
  422. return true;
  423. }
  424. else {
  425. if (task_cat.is_debug()) {
  426. task_cat.debug()
  427. << " (unable to cancel " << *this << ")\n";
  428. }
  429. return false;
  430. }
  431. }
  432. return false;
  433. }
  434. /**
  435. * Override this function to return true if the task can be successfully
  436. * executed, false if it cannot. Mainly intended as a sanity check when
  437. * attempting to add the task to a task manager.
  438. *
  439. * This function is called with the lock held.
  440. */
  441. bool PythonTask::
  442. is_runnable() {
  443. return _function != Py_None;
  444. }
  445. /**
  446. * Override this function to do something useful for the task.
  447. *
  448. * This function is called with the lock *not* held.
  449. */
  450. AsyncTask::DoneStatus PythonTask::
  451. do_task() {
  452. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  453. // Use PyGILState to protect this asynchronous call.
  454. PyGILState_STATE gstate;
  455. gstate = PyGILState_Ensure();
  456. #endif
  457. DoneStatus result = do_python_task();
  458. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  459. PyGILState_Release(gstate);
  460. #endif
  461. return result;
  462. }
  463. /**
  464. * The Python calls that implement do_task(). This function is separate so we
  465. * can acquire the Python interpretor lock while it runs.
  466. */
  467. AsyncTask::DoneStatus PythonTask::
  468. do_python_task() {
  469. PyObject *result = nullptr;
  470. // Are we waiting for a future to finish? Short-circuit all the logic below
  471. // by simply calling done().
  472. {
  473. PyObject *fut_waiter = _fut_waiter;
  474. if (fut_waiter != nullptr) {
  475. PyObject *is_done = PyObject_CallMethod(fut_waiter, "done", nullptr);
  476. if (is_done == nullptr) {
  477. return DS_interrupt;
  478. }
  479. if (!PyObject_IsTrue(is_done)) {
  480. // Nope, ask again next frame.
  481. Py_DECREF(is_done);
  482. return DS_cont;
  483. }
  484. Py_DECREF(is_done);
  485. Py_DECREF(fut_waiter);
  486. _fut_waiter = nullptr;
  487. }
  488. }
  489. if (_generator == nullptr) {
  490. // We are calling the function directly.
  491. nassertr(_function != nullptr, DS_interrupt);
  492. PyObject *args = get_args();
  493. result = PythonThread::call_python_func(_function, args);
  494. Py_DECREF(args);
  495. if (result != nullptr && PyGen_Check(result)) {
  496. // The function has yielded a generator. We will call into that
  497. // henceforth, instead of calling the function from the top again.
  498. if (task_cat.is_debug()) {
  499. PyObject *str = PyObject_ASCII(_function);
  500. task_cat.debug()
  501. << PyUnicode_AsUTF8(str) << " in " << *this
  502. << " yielded a generator.\n";
  503. Py_DECREF(str);
  504. }
  505. _generator = result;
  506. result = nullptr;
  507. } else if (result != nullptr && Py_TYPE(result)->tp_as_async != nullptr) {
  508. // The function yielded a coroutine, or something of the sort.
  509. if (task_cat.is_debug()) {
  510. PyObject *str = PyObject_ASCII(_function);
  511. PyObject *str2 = PyObject_ASCII(result);
  512. task_cat.debug()
  513. << PyUnicode_AsUTF8(str) << " in " << *this
  514. << " yielded an awaitable: " << PyUnicode_AsUTF8(str2) << "\n";
  515. Py_DECREF(str);
  516. Py_DECREF(str2);
  517. }
  518. if (PyCoro_CheckExact(result)) {
  519. // If a coroutine, am_await is possible but senseless, since we can
  520. // just call send(None) on the coroutine itself.
  521. _generator = result;
  522. } else {
  523. unaryfunc await = Py_TYPE(result)->tp_as_async->am_await;
  524. _generator = await(result);
  525. Py_DECREF(result);
  526. }
  527. result = nullptr;
  528. }
  529. }
  530. if (_generator != nullptr) {
  531. if (!_must_cancel) {
  532. // We are calling a generator. Use "send" rather than PyIter_Next since
  533. // we need to be able to read the value from a StopIteration exception.
  534. PyObject *func = PyObject_GetAttrString(_generator, "send");
  535. nassertr(func != nullptr, DS_interrupt);
  536. result = PyObject_CallOneArg(func, Py_None);
  537. Py_DECREF(func);
  538. } else {
  539. // Throw a CancelledError into the generator.
  540. _must_cancel = false;
  541. PyObject *exc = PyObject_CallNoArgs(Extension<AsyncFuture>::get_cancelled_error_type());
  542. PyObject *func = PyObject_GetAttrString(_generator, "throw");
  543. result = PyObject_CallFunctionObjArgs(func, exc, nullptr);
  544. Py_DECREF(func);
  545. Py_DECREF(exc);
  546. }
  547. if (result == nullptr) {
  548. // An error happened. If StopIteration, that indicates the task has
  549. // returned. Otherwise, we need to save it so that it can be re-raised
  550. // in the function that awaited this task.
  551. Py_DECREF(_generator);
  552. _generator = nullptr;
  553. #if PY_VERSION_HEX >= 0x030D0000 // Python 3.13
  554. // Python 3.13 does not support _PyGen_FetchStopIterationValue anymore.
  555. if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
  556. PyObject *exc = PyErr_GetRaisedException();
  557. result = ((PyStopIterationObject *)exc)->value;
  558. if (result == nullptr) {
  559. result = Py_None;
  560. }
  561. Py_INCREF(result);
  562. Py_DECREF(exc);
  563. #else
  564. if (_PyGen_FetchStopIterationValue(&result) == 0) {
  565. #endif
  566. PyErr_Clear();
  567. if (_must_cancel) {
  568. // Task was cancelled right before finishing. Make sure it is not
  569. // getting rerun or marked as successfully completed.
  570. _state = S_servicing_removed;
  571. }
  572. // If we passed a coroutine into the task, eg. something like:
  573. // taskMgr.add(my_async_function())
  574. // then we cannot rerun the task, so the return value is always
  575. // assumed to be DS_done. Instead, we pass the return value to the
  576. // result of the `await` expression.
  577. if (_function == nullptr) {
  578. if (task_cat.is_debug()) {
  579. task_cat.debug()
  580. << *this << " received StopIteration from coroutine.\n";
  581. }
  582. // Store the result in _exc_value because that's not used anyway.
  583. Py_XDECREF(_exc_value);
  584. _exc_value = result;
  585. return DS_done;
  586. }
  587. } else if (PyErr_ExceptionMatches(Extension<AsyncFuture>::get_cancelled_error_type())) {
  588. // Someone cancelled the coroutine, and it did not bother to handle it,
  589. // so we should consider it cancelled.
  590. if (task_cat.is_debug()) {
  591. task_cat.debug()
  592. << *this << " was cancelled and did not catch CancelledError.\n";
  593. }
  594. _state = S_servicing_removed;
  595. PyErr_Clear();
  596. return DS_done;
  597. } else if (_function == nullptr) {
  598. // We got an exception. If this is a scheduled coroutine, we will
  599. // keep it and instead throw it into whatever 'awaits' this task.
  600. // Otherwise, fall through and handle it the regular way.
  601. Py_XDECREF(_exception);
  602. Py_XDECREF(_exc_value);
  603. Py_XDECREF(_exc_traceback);
  604. PyErr_Fetch(&_exception, &_exc_value, &_exc_traceback);
  605. _retrieved_exception = false;
  606. if (task_cat.is_debug()) {
  607. if (_exception != nullptr && Py_TYPE(_exception) == &PyType_Type) {
  608. task_cat.debug()
  609. << *this << " received " << ((PyTypeObject *)_exception)->tp_name << " from coroutine.\n";
  610. } else {
  611. task_cat.debug()
  612. << *this << " received exception from coroutine.\n";
  613. }
  614. }
  615. // Tell the task chain we want to kill ourselves. We indicate this is
  616. // a "clean exit" because we still want to run the done callbacks on
  617. // exception.
  618. return DS_done;
  619. }
  620. } else if (result == Py_None) {
  621. // Bare yield means to continue next frame.
  622. Py_DECREF(result);
  623. return DS_cont;
  624. } else if (DtoolInstance_Check(result)) {
  625. // We are waiting for an AsyncFuture (eg. other task) to finish.
  626. AsyncFuture *fut = (AsyncFuture *)DtoolInstance_UPCAST(result, Dtool_AsyncFuture);
  627. if (fut != nullptr) {
  628. // Suspend execution of this task until this other task has completed.
  629. if (fut != (AsyncFuture *)this && !fut->done()) {
  630. if (fut->is_task()) {
  631. // This is actually a task, do we need to schedule it with the
  632. // manager? This allows doing something like
  633. // await Task.pause(1.0)
  634. // directly instead of having to do:
  635. // await taskMgr.add(Task.pause(1.0))
  636. AsyncTask *task = (AsyncTask *)fut;
  637. if (!task->is_alive()) {
  638. _manager->add(task);
  639. }
  640. }
  641. if (fut->add_waiting_task(this)) {
  642. if (task_cat.is_debug()) {
  643. task_cat.debug()
  644. << *this << " is now awaiting <" << *fut << ">.\n";
  645. }
  646. } else {
  647. // The task is already done. Continue at next opportunity.
  648. if (task_cat.is_debug()) {
  649. task_cat.debug()
  650. << *this << " would await <" << *fut << ">, were it not already done.\n";
  651. }
  652. Py_DECREF(result);
  653. return DS_cont;
  654. }
  655. } else {
  656. // This is an error. If we wanted to be fancier we could also
  657. // detect deeper circular dependencies.
  658. task_cat.error()
  659. << *this << " cannot await itself\n";
  660. }
  661. // Store the Python object in case we need to cancel it (it may be a
  662. // subclass of AsyncFuture that overrides cancel() from Python)
  663. _fut_waiter = result;
  664. return DS_await;
  665. }
  666. } else {
  667. // We are waiting for a non-Panda future to finish. We currently
  668. // implement this by checking every frame whether the future is done.
  669. PyObject *check = PyObject_GetAttrString(result, "_asyncio_future_blocking");
  670. if (check != nullptr && check != Py_None) {
  671. Py_DECREF(check);
  672. // Next frame, check whether this future is done.
  673. PyObject *fut_done = PyObject_GetAttrString(result, "done");
  674. if (fut_done == nullptr || !PyCallable_Check(fut_done)) {
  675. Py_XDECREF(fut_done);
  676. task_cat.error()
  677. << "future.done is not callable\n";
  678. return DS_interrupt;
  679. }
  680. if (task_cat.is_debug()) {
  681. PyObject *str = PyObject_ASCII(result);
  682. task_cat.debug()
  683. << *this << " is now polling " << PyUnicode_AsUTF8(str) << ".done()\n";
  684. Py_DECREF(str);
  685. }
  686. _fut_waiter = result;
  687. return DS_cont;
  688. }
  689. PyErr_Clear();
  690. Py_XDECREF(check);
  691. }
  692. }
  693. if (result == nullptr) {
  694. if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_SystemExit)) {
  695. // Don't print an error message for SystemExit. Or rather, make it a
  696. // debug message.
  697. if (task_cat.is_debug()) {
  698. task_cat.debug()
  699. << "SystemExit occurred in " << *this << "\n";
  700. }
  701. } else {
  702. task_cat.error()
  703. << "Exception occurred in " << *this << "\n";
  704. }
  705. return DS_interrupt;
  706. }
  707. if (result == Py_None || _ignore_return) {
  708. Py_DECREF(result);
  709. return DS_done;
  710. }
  711. if (PyLong_Check(result)) {
  712. long retval = PyLong_AS_LONG(result);
  713. switch (retval) {
  714. case DS_again:
  715. Py_XDECREF(_generator);
  716. _generator = nullptr;
  717. // Fall through.
  718. case DS_done:
  719. case DS_cont:
  720. case DS_pickup:
  721. case DS_exit:
  722. case DS_pause:
  723. // Legitimate value.
  724. Py_DECREF(result);
  725. return (DoneStatus) retval;
  726. case -1:
  727. // Legacy value.
  728. Py_DECREF(result);
  729. return DS_done;
  730. default:
  731. // Unexpected value.
  732. break;
  733. }
  734. }
  735. // This is unfortunate, but some are returning task.done, which nowadays
  736. // conflicts with the AsyncFuture method. Check if that is being returned.
  737. PyMethodDef *meth = nullptr;
  738. if (PyCFunction_Check(result)) {
  739. meth = ((PyCFunctionObject *)result)->m_ml;
  740. } else if (Py_TYPE(result) == &PyMethodDescr_Type) {
  741. meth = ((PyMethodDescrObject *)result)->d_method;
  742. }
  743. if (meth != nullptr && strcmp(meth->ml_name, "done") == 0) {
  744. Py_DECREF(result);
  745. return DS_done;
  746. }
  747. std::ostringstream strm;
  748. PyObject *str = PyObject_ASCII(result);
  749. if (str == nullptr) {
  750. str = PyUnicode_FromString("<repr error>");
  751. }
  752. strm
  753. << *this << " returned " << PyUnicode_AsUTF8(str);
  754. Py_DECREF(str);
  755. Py_DECREF(result);
  756. std::string message = strm.str();
  757. nassert_raise(message);
  758. return DS_interrupt;
  759. }
  760. /**
  761. * Override this function to do something useful when the task has been added
  762. * to the active queue.
  763. *
  764. * This function is called with the lock *not* held.
  765. */
  766. void PythonTask::
  767. upon_birth(AsyncTaskManager *manager) {
  768. AsyncTask::upon_birth(manager);
  769. register_to_owner();
  770. }
  771. /**
  772. * Override this function to do something useful when the task has been
  773. * removed from the active queue. The parameter clean_exit is true if the
  774. * task has been removed because it exited normally (returning DS_done), or
  775. * false if it was removed for some other reason (e.g.
  776. * AsyncTaskManager::remove()). By the time this method is called, _manager
  777. * has been cleared, so the parameter manager indicates the original
  778. * AsyncTaskManager that owned this task.
  779. *
  780. * The normal behavior is to throw the done_event only if clean_exit is true.
  781. *
  782. * This function is called with the lock *not* held.
  783. */
  784. void PythonTask::
  785. upon_death(AsyncTaskManager *manager, bool clean_exit) {
  786. AsyncTask::upon_death(manager, clean_exit);
  787. // If we were polling something when we were removed, get rid of it.
  788. //TODO: should we call cancel() on it?
  789. if (_fut_waiter != nullptr) {
  790. Py_DECREF(_fut_waiter);
  791. _fut_waiter = nullptr;
  792. }
  793. if (_upon_death != Py_None) {
  794. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  795. // Use PyGILState to protect this asynchronous call.
  796. PyGILState_STATE gstate;
  797. gstate = PyGILState_Ensure();
  798. #endif
  799. call_function(_upon_death);
  800. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  801. PyGILState_Release(gstate);
  802. #endif
  803. }
  804. unregister_from_owner();
  805. }
  806. /**
  807. * Tells the owner we are now his task.
  808. */
  809. void PythonTask::
  810. register_to_owner() {
  811. if (_owner != Py_None && !_registered_to_owner) {
  812. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  813. // Use PyGILState to protect this asynchronous call.
  814. PyGILState_STATE gstate;
  815. gstate = PyGILState_Ensure();
  816. #endif
  817. _registered_to_owner = true;
  818. call_owner_method("_addTask");
  819. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  820. PyGILState_Release(gstate);
  821. #endif
  822. }
  823. }
  824. /**
  825. * Tells the owner we are no longer his task.
  826. */
  827. void PythonTask::
  828. unregister_from_owner() {
  829. // make sure every call to _clearTask corresponds to a call to _addTask
  830. if (_owner != Py_None && _registered_to_owner) {
  831. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  832. // Use PyGILState to protect this asynchronous call.
  833. PyGILState_STATE gstate;
  834. gstate = PyGILState_Ensure();
  835. #endif
  836. _registered_to_owner = false;
  837. call_owner_method("_clearTask");
  838. #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
  839. PyGILState_Release(gstate);
  840. #endif
  841. }
  842. }
  843. /**
  844. * Calls the indicated method name on the given object, if defined, passing in
  845. * the task object as the only parameter.
  846. */
  847. void PythonTask::
  848. call_owner_method(const char *method_name) {
  849. if (_owner != Py_None) {
  850. PyObject *func = PyObject_GetAttrString(_owner, (char *)method_name);
  851. if (func == nullptr) {
  852. task_cat.error()
  853. << "Owner object added to " << *this << " has no method "
  854. << method_name << "().\n";
  855. } else {
  856. call_function(func);
  857. Py_DECREF(func);
  858. }
  859. }
  860. }
  861. /**
  862. * Calls the indicated Python function, passing in the task object as the only
  863. * parameter.
  864. */
  865. void PythonTask::
  866. call_function(PyObject *function) {
  867. if (function != Py_None) {
  868. this->ref();
  869. PyObject *self = DTool_CreatePyInstance(this, Dtool_PythonTask, true, false);
  870. PyObject *result = PyObject_CallOneArg(function, self);
  871. Py_XDECREF(result);
  872. Py_DECREF(self);
  873. }
  874. }
  875. #endif // HAVE_PYTHON