|
|
@@ -97,9 +97,17 @@ PythonTask::
|
|
|
_exc_traceback = nullptr;
|
|
|
}
|
|
|
|
|
|
+ PyObject *self = __self__;
|
|
|
+ if (self != nullptr) {
|
|
|
+ PyObject_GC_UnTrack(self);
|
|
|
+ __self__ = nullptr;
|
|
|
+ Py_DECREF(self);
|
|
|
+ }
|
|
|
+
|
|
|
+ // All of these may have already been cleared by __clear__.
|
|
|
Py_XDECREF(_function);
|
|
|
- Py_DECREF(_args);
|
|
|
- Py_DECREF(__dict__);
|
|
|
+ Py_XDECREF(_args);
|
|
|
+ Py_XDECREF(__dict__);
|
|
|
Py_XDECREF(_exception);
|
|
|
Py_XDECREF(_exc_value);
|
|
|
Py_XDECREF(_exc_traceback);
|
|
|
@@ -364,14 +372,13 @@ __getattribute__(PyObject *self, PyObject *attr) const {
|
|
|
*/
|
|
|
int PythonTask::
|
|
|
__traverse__(visitproc visit, void *arg) {
|
|
|
-/*
|
|
|
+ Py_VISIT(__self__);
|
|
|
Py_VISIT(_function);
|
|
|
Py_VISIT(_args);
|
|
|
Py_VISIT(_upon_death);
|
|
|
Py_VISIT(_owner);
|
|
|
Py_VISIT(__dict__);
|
|
|
Py_VISIT(_generator);
|
|
|
-*/
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
@@ -380,17 +387,58 @@ __traverse__(visitproc visit, void *arg) {
|
|
|
*/
|
|
|
int PythonTask::
|
|
|
__clear__() {
|
|
|
-/*
|
|
|
Py_CLEAR(_function);
|
|
|
Py_CLEAR(_args);
|
|
|
Py_CLEAR(_upon_death);
|
|
|
Py_CLEAR(_owner);
|
|
|
Py_CLEAR(__dict__);
|
|
|
Py_CLEAR(_generator);
|
|
|
-*/
|
|
|
+
|
|
|
+ Py_CLEAR(__self__);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ *
|
|
|
+ */
|
|
|
+bool PythonTask::
|
|
|
+unref() const {
|
|
|
+ if (!AsyncTask::unref()) {
|
|
|
+ // It was cleaned up by the Python garbage collector.
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If the last reference to the object is the one being held by Python,
|
|
|
+ // check whether the Python wrapper itself is also at a refcount of 1.
|
|
|
+ bool result = true;
|
|
|
+ if (get_ref_count() == 1) {
|
|
|
+ PyGILState_STATE gstate = PyGILState_Ensure();
|
|
|
+
|
|
|
+ // Check whether we have a Python wrapper. This is not the case if the
|
|
|
+ // object has been created by C++ and never been exposed to Python code.
|
|
|
+ PyObject *self = __self__;
|
|
|
+ if (self != nullptr) {
|
|
|
+ int ref_count = Py_REFCNT(self);
|
|
|
+ assert(ref_count > 0);
|
|
|
+ if (ref_count == 1) {
|
|
|
+ // The last reference to the Python wrapper is being held by us.
|
|
|
+ // Break the reference cycle and allow the object to go away.
|
|
|
+ if (!AsyncTask::unref()) {
|
|
|
+ PyObject_GC_UnTrack(self);
|
|
|
+ ((Dtool_PyInstDef *)self)->_memory_rules = false;
|
|
|
+ __self__ = nullptr;
|
|
|
+ Py_DECREF(self);
|
|
|
+
|
|
|
+ // Let the caller destroy the object.
|
|
|
+ result = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ PyGILState_Release(gstate);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Cancels this task. This is equivalent to remove(), except for coroutines,
|
|
|
* for which it will throw an exception into any currently pending await.
|