summaryrefslogtreecommitdiff
path: root/src/bindings/mono/eo_mono
diff options
context:
space:
mode:
authorVitor Sousa <vitorsousa@expertisesolutions.com.br>2019-05-31 17:43:11 -0300
committerVitor Sousa <vitorsousa@expertisesolutions.com.br>2019-05-31 17:44:12 -0300
commitfcf5f1d2e2d9ce877d550dd3352bbd6e0527299e (patch)
tree419d1e22dddf6d9283ce05cd92817df1a6371fa9 /src/bindings/mono/eo_mono
parent937da0b12c5b2b07a173199078856c35becdb0e0 (diff)
csharp: Refactor wrapper lifetime.
Summary: This commit makes use of the `ownership,shared` and `ownership,unique` events from Efl.Object in order to avoid the C# wrapper from being collected while C code holds a reference to the object. For example, creating a list of items in a for loop and attaching events to them would fails without this commit, as the C# GC may collect the wrapper. The basic idea is that we use a `WrapperSupervisor`, which is stored in the Eo data storage, with a GCHandle allocated for the lifetime of the underlying Eo object. This supervisor takes care of holding either a weak C# reference (when in unique mode, allowing the wrapper to be GC'd) or a hard C# reference (when in shared mode, making the wrapper non-collectable while the Eo has extra references). One limitation is that object graphs can leak if a shared object in the graph - an Eo child for example - stores a hard reference to another object in the graph as a C# field. In this example, this causes the parent to always have a hard C# reference (from the child) as the child is non-collectable due to the parent holding an Eo reference to it. Depends on D8678 Test Plan: `ninja test` and `make test` Reviewers: lauromoura, felipealmeida, woohyun, segfaultxavi Reviewed By: lauromoura Subscribers: cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D9014
Diffstat (limited to 'src/bindings/mono/eo_mono')
-rw-r--r--src/bindings/mono/eo_mono/EoWrapper.cs253
-rw-r--r--src/bindings/mono/eo_mono/WrapperSupervisor.cs64
-rw-r--r--src/bindings/mono/eo_mono/iwrapper.cs197
-rw-r--r--src/bindings/mono/eo_mono/meson.build4
-rw-r--r--src/bindings/mono/eo_mono/workaround.cs9
5 files changed, 409 insertions, 118 deletions
diff --git a/src/bindings/mono/eo_mono/EoWrapper.cs b/src/bindings/mono/eo_mono/EoWrapper.cs
new file mode 100644
index 0000000..b6ea619
--- /dev/null
+++ b/src/bindings/mono/eo_mono/EoWrapper.cs
@@ -0,0 +1,253 @@
1using System;
2using System.Runtime.InteropServices;
3using System.Runtime.CompilerServices;
4using System.Threading;
5
6namespace Efl
7{
8
9namespace Eo
10{
11
12public abstract class EoWrapper : IWrapper, IDisposable
13{
14 protected readonly object eventLock = new object();
15 protected bool inherited = false;
16 protected System.IntPtr handle = IntPtr.Zero;
17
18 private static Efl.EventCb ownershipUniqueDelegate = new Efl.EventCb(OwnershipUniqueCallback);
19 private static Efl.EventCb ownershipSharedDelegate = new Efl.EventCb(OwnershipSharedCallback);
20
21 /// <summary>Initializes a new instance of the <see cref="Object"/> class.
22 /// Internal usage: Constructs an instance from a native pointer. This is used when interacting with C code and should not be used directly.</summary>
23 /// <param name="raw">The native pointer to be wrapped.</param>
24 protected EoWrapper(System.IntPtr raw)
25 {
26 handle = raw;
27 AddWrapperSupervisor();
28 }
29
30 /// <summary>Initializes a new instance of the <see cref="Object"/> class.
31 /// Internal usage: Constructor to actually call the native library constructors. C# subclasses
32 /// must use the public constructor only.</summary>
33 /// <param name="baseKlass">The pointer to the base native Eo class.</param>
34 /// <param name="managedType">The managed type of the public constructor that originated this call.</param>
35 /// <param name="parent">The Efl.Object parent of this instance.</param>
36 /// <param name="file">Name of the file from where the constructor is called.</param>
37 /// <param name="line">Number of the line from where the constructor is called.</param>
38 protected EoWrapper(IntPtr baseKlass, System.Type managedType, Efl.Object parent,
39 [CallerFilePath] string file = null,
40 [CallerLineNumber] int line = 0)
41 {
42 inherited = ((object)this).GetType() != managedType;
43 IntPtr actual_klass = baseKlass;
44 if (inherited)
45 {
46 actual_klass = Efl.Eo.ClassRegister.GetInheritKlassOrRegister(baseKlass, ((object)this).GetType());
47 }
48
49 // Creation of the unfinalized Eo handle
50 Eina.Log.Debug($"Instantiating from klass 0x{actual_klass.ToInt64():x}");
51 System.IntPtr parent_ptr = System.IntPtr.Zero;
52 if (parent != null)
53 {
54 parent_ptr = parent.NativeHandle;
55 }
56
57 handle = Efl.Eo.Globals._efl_add_internal_start(file, line, actual_klass, parent_ptr, 1, 0);
58 if (handle == System.IntPtr.Zero)
59 {
60 throw new Exception("Instantiation failed");
61 }
62
63 Eina.Log.Debug($"Eo instance right after internal_start 0x{handle.ToInt64():x} with refcount {Efl.Eo.Globals.efl_ref_count(handle)}");
64 Eina.Log.Debug($"Parent was 0x{parent_ptr.ToInt64()}");
65
66 // Creation of wrapper supervisor
67 AddWrapperSupervisor();
68 }
69
70 /// <summary>Destructor.</summary>
71 ~EoWrapper()
72 {
73 Dispose(false);
74 }
75
76 /// <summary>Pointer to the native instance.</summary>
77 public System.IntPtr NativeHandle
78 {
79 get { return handle; }
80 }
81
82 /// <summary>Pointer to the native class description.</summary>
83 public abstract System.IntPtr NativeClass
84 {
85 get;
86 }
87
88 /// <summary>Releases the underlying native instance.</summary>
89 protected virtual void Dispose(bool disposing)
90 {
91 if (disposing && handle != System.IntPtr.Zero)
92 {
93 IntPtr h = handle;
94 handle = IntPtr.Zero;
95 Efl.Eo.Globals.efl_mono_native_dispose(h);
96 }
97 else
98 {
99 Monitor.Enter(Efl.All.InitLock);
100 if (Efl.All.MainLoopInitialized)
101 {
102 Efl.Eo.Globals.efl_mono_thread_safe_native_dispose(handle);
103 }
104
105 Monitor.Exit(Efl.All.InitLock);
106 }
107 }
108
109 /// <summary>Turns the native pointer into a string representation.</summary>
110 /// <returns>A string with the type and the native pointer for this object.</returns>
111 public override String ToString()
112 {
113 return $"{this.GetType().Name}@[0x{(UInt64)handle:x}]";
114 }
115
116 /// <summary>Releases the underlying native instance.</summary>
117 public void Dispose()
118 {
119 Dispose(true);
120 GC.SuppressFinalize(this);
121 }
122
123 /// <summary>Releases the underlying Eo object.
124 ///
125 /// This method is a C# counterpart to the C `efl_del` function. It removes the parent of the object
126 /// and releases the Eo reference it was holding.
127 /// </summary>
128 public void Del()
129 {
130 // FIXME Implement this
131 ((Efl.Object)this).SetParent(null);
132 Dispose();
133 }
134
135 /// <summary>Finishes instantiating this object.
136 /// Internal usage by generated code.</summary>
137 protected void FinishInstantiation()
138 {
139 Eina.Log.Debug("calling efl_add_internal_end");
140 var h = Efl.Eo.Globals._efl_add_end(handle, 1, 0);
141 Eina.Log.Debug($"efl_add_end returned eo 0x{handle.ToInt64():x}");
142
143 // if (h == IntPtr.Zero) // TODO
144 // {
145 // }
146
147 handle = h;
148 }
149
150 /// <summary>Adds a new event handler, registering it to the native event. For internal use only.</summary>
151 /// <param name="lib">The name of the native library definining the event.</param>
152 /// <param name="key">The name of the native event.</param>
153 /// <param name="evtCaller">Delegate to be called by native code on event raising.</param>
154 /// <param name="evtDelegate">Managed delegate that will be called by evtCaller on event raising.</param>
155 protected void AddNativeEventHandler(string lib, string key, Efl.EventCb evtCaller, object evtDelegate)
156 {
157 IntPtr desc = Efl.EventDescription.GetNative(lib, key);
158 if (desc == IntPtr.Zero)
159 {
160 Eina.Log.Error($"Failed to get native event {key}");
161 return;
162 }
163
164 var wsPtr = Efl.Eo.Globals.efl_mono_wrapper_supervisor_get(handle);
165 var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(wsPtr);
166 if (ws.EoEvents.ContainsKey((desc, evtDelegate)))
167 {
168 Eina.Log.Warning($"Event proxy for event {key} already registered!");
169 return;
170 }
171
172 IntPtr evtCallerPtr = Marshal.GetFunctionPointerForDelegate(evtCaller);
173 if (!Efl.Eo.Globals.efl_event_callback_priority_add(handle, desc, 0, evtCallerPtr, wsPtr))
174 {
175 Eina.Log.Error($"Failed to add event proxy for event {key}");
176 return;
177 }
178
179 ws.EoEvents[(desc, evtDelegate)] = (evtCallerPtr, evtCaller);
180 Eina.Error.RaiseIfUnhandledException();
181 }
182
183 /// <summary>Removes the given event handler for the given event. For internal use only.</summary>
184 /// <param name="lib">The name of the native library definining the event.</param>
185 /// <param name="key">The name of the native event.</param>
186 /// <param name="evtDelegate">The delegate to be removed.</param>
187 protected void RemoveNativeEventHandler(string lib, string key, object evtDelegate)
188 {
189 IntPtr desc = Efl.EventDescription.GetNative(lib, key);
190 if (desc == IntPtr.Zero)
191 {
192 Eina.Log.Error($"Failed to get native event {key}");
193 return;
194 }
195
196 var wsPtr = Efl.Eo.Globals.efl_mono_wrapper_supervisor_get(handle);
197 var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(wsPtr);
198 var evtPair = (desc, evtDelegate);
199 if (ws.EoEvents.TryGetValue(evtPair, out var caller))
200 {
201 if (!Efl.Eo.Globals.efl_event_callback_del(handle, desc, caller.evtCallerPtr, wsPtr))
202 {
203 Eina.Log.Error($"Failed to remove event proxy for event {key}");
204 return;
205 }
206
207 ws.EoEvents.Remove(evtPair);
208 Eina.Error.RaiseIfUnhandledException();
209 }
210 else
211 {
212 Eina.Log.Error($"Trying to remove proxy for event {key} when it is not registered.");
213 }
214 }
215
216 private static void OwnershipUniqueCallback(IntPtr data, ref Efl.Event.NativeStruct evt)
217 {
218 var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(data);
219 ws.MakeUnique();
220 }
221
222 private static void OwnershipSharedCallback(IntPtr data, ref Efl.Event.NativeStruct evt)
223 {
224 var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(data);
225 ws.MakeShared();
226 }
227
228 /// <sumary>Create and set to the internal native state a C# supervisor for this Eo wrapper. For internal use only.</sumary>
229 private void AddWrapperSupervisor()
230 {
231 var ws = new Efl.Eo.WrapperSupervisor(this);
232 Efl.Eo.Globals.SetWrapperSupervisor(handle, ws);
233 if (Efl.Eo.Globals.efl_ref_count(handle) > 1)
234 {
235 ws.MakeShared();
236 }
237
238 AddOwnershipEventHandlers();
239 }
240
241 /// <summary>Register handlers to ownership events, in order to control the object lifetime. For internal use only.</summary>
242 private void AddOwnershipEventHandlers()
243 {
244 AddNativeEventHandler("eo", "_EFL_EVENT_INVALIDATE", ownershipUniqueDelegate, ownershipUniqueDelegate);
245 AddNativeEventHandler("eo", "_EFL_EVENT_OWNERSHIP_UNIQUE", ownershipUniqueDelegate, ownershipUniqueDelegate);
246 AddNativeEventHandler("eo", "_EFL_EVENT_OWNERSHIP_SHARED", ownershipSharedDelegate, ownershipSharedDelegate);
247 Eina.Error.RaiseIfUnhandledException();
248 }
249}
250
251} // namespace Global
252
253} // namespace Efl
diff --git a/src/bindings/mono/eo_mono/WrapperSupervisor.cs b/src/bindings/mono/eo_mono/WrapperSupervisor.cs
new file mode 100644
index 0000000..21ef052
--- /dev/null
+++ b/src/bindings/mono/eo_mono/WrapperSupervisor.cs
@@ -0,0 +1,64 @@
1using System;
2using EventDictionary = System.Collections.Generic.Dictionary<(System.IntPtr desc, object evtDelegate), (System.IntPtr evtCallerPtr, Efl.EventCb evtCaller)>;
3
4namespace Efl
5{
6
7namespace Eo
8{
9
10/// <summary>Observe the ownership state of an Eo wrapper and control its life-cycle.</summary>
11public class WrapperSupervisor
12{
13 private System.WeakReference weakRef;
14#pragma warning disable CS0414
15 private Efl.Eo.IWrapper sharedRef;
16#pragma warning restore CS0414
17 private EventDictionary eoEvents;
18
19 /// <summary>Create a new supervisor for the given.</summary>
20 /// <param name="obj">Efl object to be supervised.</param>
21 public WrapperSupervisor(Efl.Eo.IWrapper obj)
22 {
23 weakRef = new WeakReference(obj);
24 sharedRef = null;
25 eoEvents = new EventDictionary();
26 }
27
28 /// <summary>Efl object being supervised.</summary>
29 public Efl.Eo.IWrapper Target
30 {
31 get
32 {
33 return (Efl.Eo.IWrapper) weakRef.Target;
34 }
35 }
36
37 /// <summary>Dictionary that holds the events related with the supervised object.</summary>
38 public EventDictionary EoEvents
39 {
40 get
41 {
42 return eoEvents;
43 }
44 }
45
46 /// <summary>To be called when the object is uniquely owned by C#, removing its strong reference and making it available to garbage collection.</summary>
47 public void MakeUnique()
48 {
49 sharedRef = null;
50 }
51
52 /// <summary>To be called when the object is owned in the native library too, adding a strong reference to it and making it unavailable for garbage collection.</summary>
53 public void MakeShared()
54 {
55 if (this.Target == null)
56 throw new InvalidOperationException("Tried to make a null reference shared.");
57 sharedRef = this.Target;
58 }
59}
60
61}
62
63}
64
diff --git a/src/bindings/mono/eo_mono/iwrapper.cs b/src/bindings/mono/eo_mono/iwrapper.cs
index a410a09..9e73ca4 100644
--- a/src/bindings/mono/eo_mono/iwrapper.cs
+++ b/src/bindings/mono/eo_mono/iwrapper.cs
@@ -48,6 +48,10 @@ public class Globals
48 public delegate IntPtr 48 public delegate IntPtr
49 _efl_add_internal_start_delegate([MarshalAs(UnmanagedType.LPStr)] String file, int line, 49 _efl_add_internal_start_delegate([MarshalAs(UnmanagedType.LPStr)] String file, int line,
50 IntPtr klass, IntPtr parent, byte is_ref, byte is_fallback); 50 IntPtr klass, IntPtr parent, byte is_ref, byte is_fallback);
51
52 [DllImport(efl.Libs.CustomExports)] public static extern IntPtr efl_mono_wrapper_supervisor_get(IntPtr eo);
53 [DllImport(efl.Libs.CustomExports)] public static extern void efl_mono_wrapper_supervisor_set(IntPtr eo, IntPtr ws);
54
51 [DllImport(efl.Libs.Eo)] public static extern IntPtr 55 [DllImport(efl.Libs.Eo)] public static extern IntPtr
52 _efl_add_internal_start([MarshalAs(UnmanagedType.LPStr)] String file, int line, 56 _efl_add_internal_start([MarshalAs(UnmanagedType.LPStr)] String file, int line,
53 IntPtr klass, IntPtr parent, byte is_ref, byte is_fallback); 57 IntPtr klass, IntPtr parent, byte is_ref, byte is_fallback);
@@ -68,11 +72,11 @@ public class Globals
68 [DllImport(efl.Libs.Eo)] public static extern int 72 [DllImport(efl.Libs.Eo)] public static extern int
69 efl_ref_count(IntPtr eo); 73 efl_ref_count(IntPtr eo);
70 [DllImport(efl.Libs.CustomExports)] public static extern void 74 [DllImport(efl.Libs.CustomExports)] public static extern void
71 efl_mono_gchandle_callbacks_set(Efl.FreeGCHandleCb freeGCHandleCb, Efl.RemoveEventsCb removeEventsCb); 75 efl_mono_wrapper_supervisor_callbacks_set(Efl.FreeWrapperSupervisorCb freeWrapperSupervisorCb);
72 [DllImport(efl.Libs.CustomExports)] public static extern void 76 [DllImport(efl.Libs.CustomExports)] public static extern void
73 efl_mono_native_dispose(IntPtr eo, IntPtr gcHandle); 77 efl_mono_native_dispose(IntPtr eo);
74 [DllImport(efl.Libs.CustomExports)] public static extern void 78 [DllImport(efl.Libs.CustomExports)] public static extern void
75 efl_mono_thread_safe_native_dispose(IntPtr eo, IntPtr gcHandle); 79 efl_mono_thread_safe_native_dispose(IntPtr eo);
76 [DllImport(efl.Libs.CustomExports)] public static extern void 80 [DllImport(efl.Libs.CustomExports)] public static extern void
77 efl_mono_thread_safe_efl_unref(IntPtr eo); 81 efl_mono_thread_safe_efl_unref(IntPtr eo);
78 82
@@ -231,7 +235,7 @@ public class Globals
231 description.version = 2; // EO_VERSION 235 description.version = 2; // EO_VERSION
232 description.name = class_name; 236 description.name = class_name;
233 description.class_type = 0; // REGULAR 237 description.class_type = 0; // REGULAR
234 description.data_size = (UIntPtr)8; 238 description.data_size = (UIntPtr)0;
235 description.class_initializer = IntPtr.Zero; 239 description.class_initializer = IntPtr.Zero;
236 description.class_constructor = IntPtr.Zero; 240 description.class_constructor = IntPtr.Zero;
237 description.class_destructor = IntPtr.Zero; 241 description.class_destructor = IntPtr.Zero;
@@ -246,6 +250,8 @@ public class Globals
246 IntPtr description_ptr = Eina.MemoryNative.Alloc(Marshal.SizeOf(description)); 250 IntPtr description_ptr = Eina.MemoryNative.Alloc(Marshal.SizeOf(description));
247 Marshal.StructureToPtr(description, description_ptr, false); 251 Marshal.StructureToPtr(description, description_ptr, false);
248 252
253 // FIXME: description_ptr seems to be leaking memory even after an eo_shutdown
254
249 var interface_list = EoG.get_efl_interfaces(type); 255 var interface_list = EoG.get_efl_interfaces(type);
250 256
251 Eina.Log.Debug($"Going to register new class named {class_name}"); 257 Eina.Log.Debug($"Going to register new class named {class_name}");
@@ -442,60 +448,26 @@ public class Globals
442 } 448 }
443 } 449 }
444 450
445 public static IntPtr instantiate_start(IntPtr klass, Efl.Object parent, 451 public static Efl.Eo.WrapperSupervisor WrapperSupervisorPtrToManaged(IntPtr wsPtr)
446 [CallerFilePath] string file = null,
447 [CallerLineNumber] int line = 0)
448 { 452 {
449 Eina.Log.Debug($"Instantiating from klass 0x{klass.ToInt64():x}"); 453 return (Efl.Eo.WrapperSupervisor) GCHandle.FromIntPtr(wsPtr).Target;
450 System.IntPtr parent_ptr = System.IntPtr.Zero;
451 if (parent != null)
452 {
453 parent_ptr = parent.NativeHandle;
454 }
455
456 System.IntPtr eo = Efl.Eo.Globals._efl_add_internal_start(file, line, klass, parent_ptr, 1, 0);
457 if (eo == System.IntPtr.Zero)
458 {
459 throw new Exception("Instantiation failed");
460 }
461
462 Eina.Log.Debug($"Eo instance right after internal_start 0x{eo.ToInt64():x} with refcount {Efl.Eo.Globals.efl_ref_count(eo)}");
463 Eina.Log.Debug($"Parent was 0x{parent_ptr.ToInt64()}");
464 return eo;
465 } 454 }
466 455
467 public static IntPtr instantiate_end(IntPtr eo) 456 public static Efl.Eo.WrapperSupervisor GetWrapperSupervisor(IntPtr eo)
468 { 457 {
469 Eina.Log.Debug("calling efl_add_internal_end"); 458 var wsPtr = Efl.Eo.Globals.efl_mono_wrapper_supervisor_get(eo);
470 eo = Efl.Eo.Globals._efl_add_end(eo, 1, 0); 459 if (wsPtr == IntPtr.Zero)
471 Eina.Log.Debug($"efl_add_end returned eo 0x{eo.ToInt64():x}");
472 return eo;
473 }
474
475 public static void PrivateDataSet(Efl.Eo.IWrapper obj)
476 {
477 Eina.Log.Debug($"Calling data_scope_get with obj {obj.NativeHandle.ToInt64():x} and klass {obj.NativeClass.ToInt64():x}");
478 IntPtr pd = Efl.Eo.Globals.efl_data_scope_get(obj.NativeHandle, obj.NativeClass);
479 { 460 {
480 GCHandle gch = GCHandle.Alloc(obj); 461 return null;
481 EolianPD epd;
482 epd.pointer = GCHandle.ToIntPtr(gch);
483 Marshal.StructureToPtr(epd, pd, false);
484 } 462 }
463
464 return WrapperSupervisorPtrToManaged(wsPtr);
485 } 465 }
486 466
487 public static Efl.Eo.IWrapper PrivateDataGet(IntPtr pd) 467 public static void SetWrapperSupervisor(IntPtr eo, Efl.Eo.WrapperSupervisor ws)
488 { 468 {
489 EolianPD epd = (EolianPD)Marshal.PtrToStructure(pd, typeof(EolianPD)); 469 GCHandle gch = GCHandle.Alloc(ws);
490 if (epd.pointer != IntPtr.Zero) 470 Efl.Eo.Globals.efl_mono_wrapper_supervisor_set(eo, GCHandle.ToIntPtr(gch));
491 {
492 GCHandle gch = GCHandle.FromIntPtr(epd.pointer);
493 return (Efl.Eo.IWrapper)gch.Target;
494 }
495 else
496 {
497 return null;
498 }
499 } 471 }
500 472
501 public static void free_dict_values(Dictionary<String, IntPtr> dict) 473 public static void free_dict_values(Dictionary<String, IntPtr> dict)
@@ -601,93 +573,101 @@ public class Globals
601 return null; 573 return null;
602 } 574 }
603 575
604 IntPtr eoKlass = efl_class_get(handle); 576 Efl.Eo.Globals.efl_ref(handle);
605 577 try
606 if (eoKlass == IntPtr.Zero)
607 { 578 {
608 throw new InvalidOperationException($"Can't get Eo class for object handle 0x{handle.ToInt64():x}"); 579 var ws = Efl.Eo.Globals.GetWrapperSupervisor(handle);
609 } 580 if (ws != null && ws.Target != null)
581 {
582 if (!shouldIncRef)
583 {
584 Efl.Eo.Globals.efl_unref(handle);
585 }
610 586
611 var managedType = ClassRegister.GetManagedType(eoKlass); 587 return ws.Target;
588 }
612 589
613 if (managedType == null) 590 IntPtr eoKlass = efl_class_get(handle);
614 {
615 IntPtr nativeName = efl_class_name_get(eoKlass);
616 var name = Eina.StringConversion.NativeUtf8ToManagedString(nativeName);
617 591
618 throw new InvalidOperationException($"Can't get Managed class for object handle 0x{handle.ToInt64():x} with native class [{name}]"); 592 if (eoKlass == IntPtr.Zero)
619 } 593 {
594 throw new InvalidOperationException($"Can't get Eo class for object handle 0x{handle.ToInt64():x}");
595 }
620 596
621 // Pure C# classes that inherit from generated classes store their C# instance in their 597 var managedType = ClassRegister.GetManagedType(eoKlass);
622 // Eo private data field.
623 if (!IsGeneratedClass(managedType))
624 {
625 Efl.Eo.IWrapper instance = null;
626 IntPtr pd = efl_data_scope_get(handle, eoKlass);
627 598
628 if (pd != IntPtr.Zero) 599 if (managedType == null)
629 { 600 {
630 instance = PrivateDataGet(pd); 601 IntPtr nativeName = efl_class_name_get(eoKlass);
631 } 602 var name = Eina.StringConversion.NativeUtf8ToManagedString(nativeName);
632 603
633 return instance; 604 throw new InvalidOperationException($"Can't get Managed class for object handle 0x{handle.ToInt64():x} with native class [{name}]");
634 } 605 }
635 606
636 System.Reflection.ConstructorInfo constructor = null; 607 Debug.Assert(IsGeneratedClass(managedType));
608 System.Reflection.ConstructorInfo constructor = null;
637 609
638 try 610 try
639 { 611 {
640 var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; 612 var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
641 constructor = managedType.GetConstructor(flags, null, new Type[1] { typeof(System.IntPtr) }, null); 613 constructor = managedType.GetConstructor(flags, null, new Type[1] { typeof(System.IntPtr) }, null);
642 } 614 }
643 catch (InvalidOperationException) 615 catch (InvalidOperationException)
644 { 616 {
645 throw new InvalidOperationException($"Can't get constructor for type {managedType}"); 617 throw new InvalidOperationException($"Can't get constructor for type {managedType}");
646 } 618 }
647 619
648 var ret = constructor.Invoke(new object[1] { handle }) as Efl.Eo.IWrapper; 620 var ret = (Efl.Eo.IWrapper) constructor.Invoke(new object[1] { handle });
649 621
650 if (ret != null && shouldIncRef) 622 if (ret == null)
651 Efl.Eo.Globals.efl_ref(handle); 623 {
624 throw new InvalidOperationException($"Can't construct type {managedType} from IntPtr handle");
625 }
652 626
653 return ret; 627 if (shouldIncRef)
654 } 628 {
629 Efl.Eo.Globals.efl_ref(handle);
630 }
655 631
656 private static Efl.FreeGCHandleCb FreeGCHandleCallbackDelegate = new Efl.FreeGCHandleCb(FreeGCHandleCallback); 632 return ret;
657 public static void FreeGCHandleCallback(IntPtr gcHandlePtr)
658 {
659 try
660 {
661 GCHandle gcHandle = GCHandle.FromIntPtr(gcHandlePtr);
662 gcHandle.Free();
663 } 633 }
664 catch (Exception e) 634 finally
665 { 635 {
666 Eina.Log.Error(e.ToString()); 636 Efl.Eo.Globals.efl_unref(handle);
667 Eina.Error.Set(Eina.Error.UNHANDLED_EXCEPTION);
668 } 637 }
669 } 638 }
670 639
671 private static Efl.RemoveEventsCb RemoveEventsCallbackDelegate = new Efl.RemoveEventsCb(RemoveEventsCallback); 640 private static Efl.FreeWrapperSupervisorCb FreeWrapperSupervisorCallbackDelegate = new Efl.FreeWrapperSupervisorCb(FreeWrapperSupervisorCallback);
672 public static void RemoveEventsCallback(IntPtr obj, IntPtr gcHandlePtr) 641 public static void FreeWrapperSupervisorCallback(IntPtr eo)
673 { 642 {
674 try 643 try
675 { 644 {
676 GCHandle gcHandle = GCHandle.FromIntPtr(gcHandlePtr); 645 var wsPtr = Efl.Eo.Globals.efl_mono_wrapper_supervisor_get(eo);
677 var eoEvents = gcHandle.Target as Dictionary<(IntPtr desc, object evtDelegate), (IntPtr evtCallerPtr, Efl.EventCb evtCaller)>; 646 if (wsPtr == IntPtr.Zero)
678 if (eoEvents == null)
679 { 647 {
680 Eina.Log.Error($"Invalid event dictionary [GCHandle pointer: {gcHandlePtr}]"); 648 Eina.Log.Error($"Invalid wrapper supervisor [Eo pointer: {eo.ToInt64():x}]");
681 return; 649 return;
682 } 650 }
683 651
684 foreach (var item in eoEvents) 652 Efl.Eo.Globals.efl_mono_wrapper_supervisor_set(eo, IntPtr.Zero);
653
654 GCHandle gch = GCHandle.FromIntPtr(wsPtr);
655 var ws = (Efl.Eo.WrapperSupervisor) gch.Target;
656 foreach (var item in ws.EoEvents)
685 { 657 {
686 if (!efl_event_callback_del(obj, item.Key.desc, item.Value.evtCallerPtr, IntPtr.Zero)) 658 if (!efl_event_callback_del(eo, item.Key.desc, item.Value.evtCallerPtr, wsPtr))
687 { 659 {
688 Eina.Log.Error($"Failed to remove event proxy for event {item.Key.desc} [cb: {item.Value.evtCallerPtr}]"); 660 Eina.Log.Error($"Failed to remove event proxy for event {item.Key.desc} [eo: {eo.ToInt64():x}; cb: {item.Value.evtCallerPtr.ToInt64():x}]");
689 } 661 }
690 } 662 }
663
664 // Free the native eo
665 Efl.Eo.Globals.efl_unref(eo);
666
667 // now the WrapperSupervisor can be collected, and so its member:
668 // - the event dictionary
669 // - and the EoWrapper if it is still pinned
670 gch.Free();
691 } 671 }
692 catch (Exception e) 672 catch (Exception e)
693 { 673 {
@@ -698,7 +678,7 @@ public class Globals
698 678
699 public static void SetNativeDisposeCallbacks() 679 public static void SetNativeDisposeCallbacks()
700 { 680 {
701 efl_mono_gchandle_callbacks_set(FreeGCHandleCallbackDelegate, RemoveEventsCallbackDelegate); 681 efl_mono_wrapper_supervisor_callbacks_set(FreeWrapperSupervisorCallbackDelegate);
702 } 682 }
703 683
704 public static void ThreadSafeFreeCbExec(EinaFreeCb cbFreeCb, IntPtr cbData) 684 public static void ThreadSafeFreeCbExec(EinaFreeCb cbFreeCb, IntPtr cbData)
@@ -800,8 +780,6 @@ public static class ClassRegister
800 string name = Eina.StringConversion.NativeUtf8ToManagedString(namePtr) 780 string name = Eina.StringConversion.NativeUtf8ToManagedString(namePtr)
801 .Replace("_", ""); // Convert Efl C name to C# name 781 .Replace("_", ""); // Convert Efl C name to C# name
802 782
803 var klass_type = Efl.Eo.Globals.efl_class_type_get(klass);
804
805 // Check if this is an internal implementation of an abstract class 783 // Check if this is an internal implementation of an abstract class
806 var abstract_impl_suffix = "Realized"; 784 var abstract_impl_suffix = "Realized";
807 if (name.EndsWith(abstract_impl_suffix)) 785 if (name.EndsWith(abstract_impl_suffix))
@@ -813,6 +791,7 @@ public static class ClassRegister
813 } 791 }
814 792
815 // When converting to managed, interfaces and mixins gets the 'I' prefix. 793 // When converting to managed, interfaces and mixins gets the 'I' prefix.
794 var klass_type = Efl.Eo.Globals.efl_class_type_get(klass);
816 if (klass_type == Efl.Eo.Globals.EflClassType.Interface || klass_type == Efl.Eo.Globals.EflClassType.Mixin) 795 if (klass_type == Efl.Eo.Globals.EflClassType.Interface || klass_type == Efl.Eo.Globals.EflClassType.Mixin)
817 { 796 {
818 var pos = name.LastIndexOf("."); 797 var pos = name.LastIndexOf(".");
diff --git a/src/bindings/mono/eo_mono/meson.build b/src/bindings/mono/eo_mono/meson.build
index 8aca400..013b1cc 100644
--- a/src/bindings/mono/eo_mono/meson.build
+++ b/src/bindings/mono/eo_mono/meson.build
@@ -2,7 +2,9 @@ mono_files += files(
2 'iwrapper.cs', 2 'iwrapper.cs',
3 'workaround.cs', 3 'workaround.cs',
4 'FunctionWrapper.cs', 4 'FunctionWrapper.cs',
5 'NativeModule.cs' 5 'NativeModule.cs',
6 'EoWrapper.cs',
7 'WrapperSupervisor.cs'
6) 8)
7 9
8if host_machine.system() == 'windows' 10if host_machine.system() == 'windows'
diff --git a/src/bindings/mono/eo_mono/workaround.cs b/src/bindings/mono/eo_mono/workaround.cs
index e32c921..9f22b90 100644
--- a/src/bindings/mono/eo_mono/workaround.cs
+++ b/src/bindings/mono/eo_mono/workaround.cs
@@ -44,12 +44,6 @@ public struct Efl_Object_Ops
44 public UIntPtr count; 44 public UIntPtr count;
45}; 45};
46 46
47[StructLayout(LayoutKind.Sequential)]
48public struct EolianPD
49{
50 public IntPtr pointer;
51}
52
53#pragma warning disable 0169 47#pragma warning disable 0169
54 48
55public struct EvasObjectBoxLayout 49public struct EvasObjectBoxLayout
@@ -115,8 +109,7 @@ public struct EventDescription
115}; 109};
116 110
117public delegate void EventCb(System.IntPtr data, ref Event.NativeStruct evt); 111public delegate void EventCb(System.IntPtr data, ref Event.NativeStruct evt);
118public delegate void FreeGCHandleCb(System.IntPtr gcHandle); 112public delegate void FreeWrapperSupervisorCb(System.IntPtr obj);
119public delegate void RemoveEventsCb(System.IntPtr obj, System.IntPtr gcHandle);
120 113
121[StructLayout(LayoutKind.Sequential)] 114[StructLayout(LayoutKind.Sequential)]
122public struct TextCursorCursor 115public struct TextCursorCursor