@@ -81,13 +81,76 @@ static PyObject *init_unreal_engine(void)
8181}
8282#endif
8383
84- std::map<UObject *, ue_PyUObject *> ue_python_gc;
84+ namespace UnrealEnginePythonHouseKeeper
85+ {
86+ std::map<UObject *, ue_PyUObject *> u_object_py_mapping;
87+
88+ struct FPythonDelegateTracker
89+ {
90+ FWeakObjectPtr owner;
91+ UPythonDelegate *delegate;
92+
93+ FPythonDelegateTracker (UPythonDelegate *delegate_to_track, UObject *delegate_owner) : owner(delegate_owner), delegate(delegate_to_track)
94+ {
95+ }
96+
97+ ~FPythonDelegateTracker ()
98+ {
99+ }
100+ };
101+
102+ std::list<FPythonDelegateTracker> py_delegates_tracker;
103+ };
104+
105+ static void ue_py_delegates_gc ()
106+ {
107+ auto &lst = UnrealEnginePythonHouseKeeper::py_delegates_tracker;
108+ for (auto itr = lst.begin (); itr != lst.end (); /* noop*/ )
109+ // for (auto itr = lst.begin(); itr != lst.end(); ++itr)
110+ {
111+ /* if (itr->owner.IsValid())
112+ {
113+ UE_LOG(LogPython, Error, TEXT("Delegate for %s"), *itr->owner.Get()->GetName());
114+ }
115+ else
116+ {
117+ UE_LOG(LogPython, Error, TEXT("Found bad delegate"));
118+ }
119+ */
120+ if (!itr->owner .IsValid ())
121+ {
122+ itr->delegate ->RemoveFromRoot ();
123+ itr = lst.erase (itr);
124+ }
125+ else
126+ ++itr;
127+ }
128+ }
129+
130+ UPythonDelegate *ue_py_new_delegate (UObject *owner, PyObject *py_callable, UFunction *signature)
131+ {
132+ // TODO: do a round of gc only if enough time passed
133+ ue_py_delegates_gc ();
134+
135+ UPythonDelegate *py_delegate = NewObject<UPythonDelegate>();
136+ if (!py_delegate)
137+ return nullptr ;
138+
139+ py_delegate->AddToRoot ();
140+ py_delegate->SetPyCallable (py_callable);
141+ py_delegate->SetSignature (signature);
142+
143+ UnrealEnginePythonHouseKeeper::FPythonDelegateTracker tracker (py_delegate, owner);
144+ UnrealEnginePythonHouseKeeper::py_delegates_tracker.push_back (tracker);
145+
146+ return py_delegate;
147+ }
85148
86149
87150static PyObject *py_unreal_engine_py_gc (PyObject * self, PyObject * args)
88151{
89152 std::list<UObject *> broken_list;
90- for (auto it : ue_python_gc )
153+ for (auto it : UnrealEnginePythonHouseKeeper::u_object_py_mapping )
91154 {
92155#if defined(UEPY_MEMORY_DEBUG)
93156 UE_LOG (LogPython, Warning, TEXT (" Checking for UObject at %p" ), it.first );
@@ -100,12 +163,17 @@ static PyObject *py_unreal_engine_py_gc(PyObject * self, PyObject * args)
100163#endif
101164 broken_list.push_back (u_obj);
102165 }
166+ else
167+ {
168+ UE_LOG (LogPython, Error, TEXT (" UObject at %p %s is in use" ), u_obj, *u_obj->GetName ());
169+ }
103170 }
171+ /*
104172 for (UObject *u_obj : broken_list)
105173 {
106- ue_PyUObject *py_obj = ue_python_gc .at (u_obj);
174+ ue_PyUObject *py_obj = UnrealEnginePythonHouseKeeper::u_object_py_mapping .at(u_obj);
107175 Py_DECREF(py_obj);
108- }
176+ }*/
109177
110178 return PyLong_FromLong (broken_list.size ());
111179
@@ -975,43 +1043,21 @@ static PyMethodDef ue_PyUObject_methods[] = {
9751043 { NULL } /* Sentinel */
9761044};
9771045
978- void ue_pydelegates_cleanup (ue_PyUObject *self)
1046+
1047+ // destructor
1048+ static void ue_pyobject_dealloc (ue_PyUObject *self)
9791049{
980- // this could happen during engine shutdown, so have mercy
981- // start deallocating delegates mapped to the object
982- if (!self || !self->python_delegates_gc )
983- return ;
984- UE_LOG (LogPython, Warning, TEXT (" Delegates = %d" ), self->python_delegates_gc ->size ());
985- for (UPythonDelegate *py_delegate : *(self->python_delegates_gc ))
986- {
987- if (py_delegate && py_delegate->IsValidLowLevel ())
988- {
1050+ PyObject_GC_UnTrack (self);
9891051#if defined(UEPY_MEMORY_DEBUG)
990- UE_LOG (LogPython, Warning, TEXT (" Removing UPythonDelegate %p from ue_PyUObject %p mapped to UObject %p" ), py_delegate , self, self->ue_object );
1052+ UE_LOG (LogPython, Warning, TEXT (" Destroying ue_PyUObject %p mapped to UObject %p" ), self, self->ue_object );
9911053#endif
992- py_delegate->RemoveFromRoot ();
993- }
994- }
995- self->python_delegates_gc ->clear ();
996- delete self->python_delegates_gc ;
997- self->python_delegates_gc = nullptr ;
998-
999- if (self->auto_rooted && self->ue_object ->IsRooted ())
1054+ if (self->auto_rooted && (self->ue_object && self->ue_object ->IsValidLowLevel () && self->ue_object ->IsRooted ()))
10001055 {
10011056 self->ue_object ->RemoveFromRoot ();
10021057 }
10031058
10041059 Py_XDECREF (self->py_dict );
1005- }
1006-
1007- // destructor
1008- static void ue_pyobject_dealloc (ue_PyUObject *self)
1009- {
1010- #if defined(UEPY_MEMORY_DEBUG)
1011- UE_LOG (LogPython, Warning, TEXT (" Destroying ue_PyUObject %p mapped to UObject %p" ), self, self->ue_object );
1012- #endif
1013- ue_pydelegates_cleanup (self);
1014- ue_python_gc.erase (self->ue_object );
1060+ UnrealEnginePythonHouseKeeper::u_object_py_mapping.erase (self->ue_object );
10151061 Py_TYPE (self)->tp_free ((PyObject *)self);
10161062}
10171063
@@ -1162,6 +1208,12 @@ static PyObject *ue_PyUObject_str(ue_PyUObject *self)
11621208#endif
11631209}
11641210
1211+ static int ue_PyUObject_traverse (ue_PyUObject *self, visitproc visit, void *arg)
1212+ {
1213+ UE_LOG (LogPython, Error, TEXT (" TRAVERSING %p" ), self);
1214+ return 0 ;
1215+ }
1216+
11651217static PyObject *ue_PyUObject_call (ue_PyUObject *self, PyObject *args, PyObject *kw)
11661218{
11671219 // if it is a class, create a new object
@@ -1277,9 +1329,9 @@ static PyTypeObject ue_PyUObjectType = {
12771329 (getattrofunc)ue_PyUObject_getattro, /* tp_getattro */
12781330 (setattrofunc)ue_PyUObject_setattro, /* tp_setattro */
12791331 0 , /* tp_as_buffer */
1280- Py_TPFLAGS_DEFAULT, /* tp_flags */
1332+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC , /* tp_flags */
12811333 " Unreal Engine generic UObject" , /* tp_doc */
1282- 0 , /* tp_traverse */
1334+ (traverseproc)ue_PyUObject_traverse , /* tp_traverse */
12831335 0 , /* tp_clear */
12841336 0 , /* tp_richcompare */
12851337 0 , /* tp_weaklistoffset */
@@ -1727,12 +1779,7 @@ static int unreal_engine_py_init(ue_PyUObject *self, PyObject *args, PyObject *k
17271779 FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer (ObjectInitializer.GetObj ());
17281780
17291781 FScriptDelegate script_delegate;
1730- UPythonDelegate *py_delegate = NewObject<UPythonDelegate>();
1731- py_delegate->SetPyCallable (mc_value);
1732- py_delegate->SetSignature (casted_prop->SignatureFunction );
1733- // avoid delegates to be destroyed by the GC
1734- py_delegate->AddToRoot ();
1735-
1782+ UPythonDelegate *py_delegate = ue_py_new_delegate (ObjectInitializer.GetObj (), mc_value, casted_prop->SignatureFunction );
17361783 // fake UFUNCTION for bypassing checks
17371784 script_delegate.BindUFunction (py_delegate, FName (" PyFakeCallable" ));
17381785
@@ -2100,21 +2147,21 @@ ue_PyUObject *ue_get_python_uobject(UObject *ue_obj)
21002147{
21012148 if (!ue_obj || !ue_obj->IsValidLowLevel () || ue_obj->IsPendingKillOrUnreachable ())
21022149 return nullptr ;
2103- std::map<UObject *, ue_PyUObject *>::iterator it = ue_python_gc .find (ue_obj);
2150+ std::map<UObject *, ue_PyUObject *>::iterator it = UnrealEnginePythonHouseKeeper::u_object_py_mapping .find (ue_obj);
21042151 // not found ??
2105- if (it == ue_python_gc .end ())
2152+ if (it == UnrealEnginePythonHouseKeeper::u_object_py_mapping .end ())
21062153 {
21072154
2108- ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_New (ue_PyUObject, &ue_PyUObjectType);
2155+ // ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_New(ue_PyUObject, &ue_PyUObjectType);
2156+ ue_PyUObject *ue_py_object = (ue_PyUObject *)PyObject_GC_New (ue_PyUObject, &ue_PyUObjectType);
21092157 if (!ue_py_object)
21102158 {
21112159 return nullptr ;
21122160 }
21132161 ue_py_object->ue_object = ue_obj;
2114- ue_py_object->python_delegates_gc = new std::list<UPythonDelegate *>;
21152162 ue_py_object->py_dict = PyDict_New ();
21162163
2117- ue_python_gc [ue_obj] = ue_py_object;
2164+ UnrealEnginePythonHouseKeeper::u_object_py_mapping [ue_obj] = ue_py_object;
21182165
21192166#if defined(UEPY_MEMORY_DEBUG)
21202167 UE_LOG (LogPython, Warning, TEXT (" CREATED UPyObject at %p for %p %s" ), ue_py_object, ue_obj, *ue_obj->GetName ());
@@ -2211,7 +2258,7 @@ void unreal_engine_py_log_error()
22112258 }
22122259
22132260 PyErr_Clear ();
2214- }
2261+ }
22152262
22162263// retrieve a UWorld from a generic UObject (if possible)
22172264UWorld *ue_get_uworld (ue_PyUObject *py_obj)
@@ -3235,13 +3282,7 @@ PyObject *ue_bind_pyevent(ue_PyUObject *u_obj, FString event_name, PyObject *py_
32353282 FMulticastScriptDelegate multiscript_delegate = casted_prop->GetPropertyValue_InContainer (u_obj->ue_object );
32363283
32373284 FScriptDelegate script_delegate;
3238- UPythonDelegate *py_delegate = NewObject<UPythonDelegate>();
3239- py_delegate->SetPyCallable (py_callable);
3240- py_delegate->SetSignature (casted_prop->SignatureFunction );
3241- // avoid delegates to be destroyed by the GC
3242- py_delegate->AddToRoot ();
3243- u_obj->python_delegates_gc ->push_back (py_delegate);
3244-
3285+ UPythonDelegate *py_delegate = ue_py_new_delegate (u_obj->ue_object , py_callable, casted_prop->SignatureFunction );
32453286 // fake UFUNCTION for bypassing checks
32463287 script_delegate.BindUFunction (py_delegate, FName (" PyFakeCallable" ));
32473288
0 commit comments