summaryrefslogtreecommitdiff
path: root/src/tests
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/tests
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/tests')
-rw-r--r--src/tests/efl_mono/Eo.cs15
-rw-r--r--src/tests/efl_mono/Inheritance.cs89
-rw-r--r--src/tests/efl_mono/TestUtils.cs28
-rw-r--r--src/tests/efl_mono/dummy_part_holder.c23
-rw-r--r--src/tests/efl_mono/dummy_part_holder.eo1
5 files changed, 142 insertions, 14 deletions
diff --git a/src/tests/efl_mono/Eo.cs b/src/tests/efl_mono/Eo.cs
index 95b6b6395e..be66f0842c 100644
--- a/src/tests/efl_mono/Eo.cs
+++ b/src/tests/efl_mono/Eo.cs
@@ -510,4 +510,19 @@ class TestProvider
510 } 510 }
511} 511}
512 512
513class TestObjectDeletion
514{
515 public static void test_object_deletion()
516 {
517 var obj = new Dummy.PartHolder();
518 var part = obj.OnePart;
519
520 Test.AssertNotNull(part);
521
522 part.Del();
523
524 Test.AssertNull(obj.OnePart);
525 }
526}
527
513} 528}
diff --git a/src/tests/efl_mono/Inheritance.cs b/src/tests/efl_mono/Inheritance.cs
index befdd3a7b4..74c1086e78 100644
--- a/src/tests/efl_mono/Inheritance.cs
+++ b/src/tests/efl_mono/Inheritance.cs
@@ -35,6 +35,46 @@ class TestInheritance
35 } 35 }
36 } 36 }
37 37
38 internal class Inherit3Parent : Dummy.TestObject
39 {
40 public bool disposed = false;
41 public bool childDisposed = false;
42
43 ~Inherit3Parent()
44 {
45 Console.WriteLine("finalizer called for parent");
46 }
47
48 protected override void Dispose(bool disposing)
49 {
50 Console.WriteLine("Dispose parent");
51 base.Dispose(disposing);
52 }
53 }
54
55 internal class Inherit3Child : Dummy.TestObject
56 {
57 Inherit3Parent parent;
58 public Inherit3Child(Inherit3Parent parent) : base(parent)
59 {
60 // WARNING: Uncommenting the line below causes the parent-child cycle to leak.
61 // The GC won't be able to collect it.
62 // this.parent = parent;
63 }
64
65 ~Inherit3Child()
66 {
67 Console.WriteLine("finalizer called for child");
68 }
69
70 protected override void Dispose(bool disposing)
71 {
72 /* parent.childDisposed = true; */
73 Console.WriteLine("Dispose parent");
74 base.Dispose(disposing);
75 }
76 }
77
38 public static void test_inherit_from_regular_class() 78 public static void test_inherit_from_regular_class()
39 { 79 {
40 var obj = new Inherit1(); 80 var obj = new Inherit1();
@@ -50,6 +90,55 @@ class TestInheritance
50 string s = Dummy.InheritHelper.ReceiveDummyAndCallInStringshare(obj); 90 string s = Dummy.InheritHelper.ReceiveDummyAndCallInStringshare(obj);
51 Test.AssertEquals ("Hello World", s); 91 Test.AssertEquals ("Hello World", s);
52 } 92 }
93
94 private static void CreateAndCheckInheritedObjects(out WeakReference parentWRef, out WeakReference childWRef)
95 {
96 var parent = new Inherit3Parent();
97 var child = new Inherit3Child(parent);
98
99 parentWRef = new WeakReference(parent);
100 childWRef = new WeakReference(child);
101
102 Console.WriteLine($"Parent [{parent.ToString()}] has {Efl.Eo.Globals.efl_ref_count(parent.NativeHandle)} refs");
103 Console.WriteLine($"Child [{child.ToString()}] has {Efl.Eo.Globals.efl_ref_count(child.NativeHandle)} refs");
104
105 child = null;
106
107 System.GC.Collect(System.GC.MaxGeneration, GCCollectionMode.Forced, true, true);
108 System.GC.WaitForPendingFinalizers();
109 Efl.App.AppMain.Iterate();
110
111 child = (Inherit3Child) childWRef.Target;
112
113 Test.AssertNotNull(parent);
114 Test.AssertNotNull(child);
115 Test.AssertEquals(false, parent.disposed);
116 Test.AssertEquals(false, parent.childDisposed);
117
118 Console.WriteLine($"Parent [{parent.ToString()}] has {Efl.Eo.Globals.efl_ref_count(parent.NativeHandle)} refs");
119 Console.WriteLine($"Child [{child.ToString()}] has {Efl.Eo.Globals.efl_ref_count(child.NativeHandle)} refs");
120
121 parent = null;
122 child = null;
123 }
124
125 public static void test_inherit_lifetime()
126 {
127 WeakReference parentWRef;
128 WeakReference childWRef;
129
130 CreateAndCheckInheritedObjects(out parentWRef, out childWRef);
131
132 // Two invocations to iterate a the child wasn't being released with a single one
133 Test.CollectAndIterate();
134 Test.CollectAndIterate();
135
136 var parent = (Dummy.TestObject) parentWRef.Target;
137 var child = (Dummy.TestObject) childWRef.Target;
138
139 Test.AssertNull(parent);
140 Test.AssertNull(child);
141 }
53} 142}
54 143
55} 144}
diff --git a/src/tests/efl_mono/TestUtils.cs b/src/tests/efl_mono/TestUtils.cs
index 657bdb0300..b66f15a0bc 100644
--- a/src/tests/efl_mono/TestUtils.cs
+++ b/src/tests/efl_mono/TestUtils.cs
@@ -43,13 +43,14 @@ public static class Test
43 [CallerFilePath] string file = null, 43 [CallerFilePath] string file = null,
44 [CallerMemberName] string member = null) 44 [CallerMemberName] string member = null)
45 { 45 {
46 if (file == null) 46 if (expected == null && actual == null)
47 file = "(unknown file)"; 47 return;
48 if (member == null) 48 if (expected == null || !expected.Equals(actual))
49 member = "(unknown member)"; 49 {
50 if (expected == null) 50 if (file == null)
51 throw new AssertionException($"{file}:{line} ({member}) Null expected value. Use AssertNull."); 51 file = "(unknown file)";
52 if (!expected.Equals(actual)) { 52 if (member == null)
53 member = "(unknown member)";
53 if (msg == null || msg.Length == 0) 54 if (msg == null || msg.Length == 0)
54 msg = $"Expected \"{expected}\", actual \"{actual}\""; 55 msg = $"Expected \"{expected}\", actual \"{actual}\"";
55 throw new AssertionException($"{file}:{line} ({member}) {msg}"); 56 throw new AssertionException($"{file}:{line} ({member}) {msg}");
@@ -62,13 +63,12 @@ public static class Test
62 [CallerFilePath] string file = null, 63 [CallerFilePath] string file = null,
63 [CallerMemberName] string member = null) 64 [CallerMemberName] string member = null)
64 { 65 {
65 if (file == null) 66 if (expected == null ? actual == null : expected.Equals(actual))
66 file = "(unknown file)"; 67 {
67 if (member == null) 68 if (file == null)
68 member = "(unknown member)"; 69 file = "(unknown file)";
69 if (expected == null) 70 if (member == null)
70 throw new AssertionException($"{file}:{line} ({member}) Null expected value. Use AssertNull."); 71 member = "(unknown member)";
71 if (expected.Equals(actual)) {
72 if (msg == null || msg.Length == 0) 72 if (msg == null || msg.Length == 0)
73 msg = $"Expected \"{expected}\" shouldn't be equal to actual \"{actual}\""; 73 msg = $"Expected \"{expected}\" shouldn't be equal to actual \"{actual}\"";
74 throw new AssertionException($"{file}:{line} ({member}) {msg}"); 74 throw new AssertionException($"{file}:{line} ({member}) {msg}");
diff --git a/src/tests/efl_mono/dummy_part_holder.c b/src/tests/efl_mono/dummy_part_holder.c
index b595ac1f8d..aaeee6cec8 100644
--- a/src/tests/efl_mono/dummy_part_holder.c
+++ b/src/tests/efl_mono/dummy_part_holder.c
@@ -6,6 +6,16 @@ typedef struct Dummy_Part_Holder_Data
6 Eo *two; 6 Eo *two;
7} Dummy_Part_Holder_Data; 7} Dummy_Part_Holder_Data;
8 8
9void part_deleted_cb(void *data, const Efl_Event *evt)
10{
11 Dummy_Part_Holder_Data *pd = data;
12
13 if (evt->object == pd->one)
14 pd->one = NULL;
15 else if (evt->object == pd->two)
16 pd->two = NULL;
17}
18
9// Part holder 19// Part holder
10static Efl_Object* 20static Efl_Object*
11_dummy_part_holder_efl_object_constructor(Eo *obj, Dummy_Part_Holder_Data *pd) 21_dummy_part_holder_efl_object_constructor(Eo *obj, Dummy_Part_Holder_Data *pd)
@@ -16,12 +26,25 @@ _dummy_part_holder_efl_object_constructor(Eo *obj, Dummy_Part_Holder_Data *pd)
16 if (!efl_parent_get(obj)) 26 if (!efl_parent_get(obj))
17 { 27 {
18 pd->one = efl_add(DUMMY_TEST_OBJECT_CLASS, obj, efl_name_set(efl_added, "part_one")); 28 pd->one = efl_add(DUMMY_TEST_OBJECT_CLASS, obj, efl_name_set(efl_added, "part_one"));
29 efl_event_callback_add(pd->one, EFL_EVENT_DEL, part_deleted_cb, pd);
19 pd->two = efl_add(DUMMY_TEST_OBJECT_CLASS, obj, efl_name_set(efl_added, "part_two")); 30 pd->two = efl_add(DUMMY_TEST_OBJECT_CLASS, obj, efl_name_set(efl_added, "part_two"));
31 efl_event_callback_add(pd->two, EFL_EVENT_DEL, part_deleted_cb, pd);
32
33
20 } 34 }
21 35
22 return obj; 36 return obj;
23} 37}
24 38
39static void
40_dummy_part_holder_efl_object_destructor(EINA_UNUSED Eo* obj, Dummy_Part_Holder_Data *pd)
41{
42 if (pd->one)
43 efl_parent_set(pd->one, NULL);
44 if (pd->two)
45 efl_parent_set(pd->two, NULL);
46}
47
25Efl_Object *_dummy_part_holder_efl_part_part_get(EINA_UNUSED const Eo *obj, Dummy_Part_Holder_Data *pd, const char *name) 48Efl_Object *_dummy_part_holder_efl_part_part_get(EINA_UNUSED const Eo *obj, Dummy_Part_Holder_Data *pd, const char *name)
26{ 49{
27 if (!strcmp(name, "one")) 50 if (!strcmp(name, "one"))
diff --git a/src/tests/efl_mono/dummy_part_holder.eo b/src/tests/efl_mono/dummy_part_holder.eo
index d65fd12e5b..b95f1a5cdc 100644
--- a/src/tests/efl_mono/dummy_part_holder.eo
+++ b/src/tests/efl_mono/dummy_part_holder.eo
@@ -9,5 +9,6 @@ class @beta Dummy.Part_Holder extends Dummy.Test_Object implements Efl.Part {
9 implements { 9 implements {
10 Efl.Part.part_get; 10 Efl.Part.part_get;
11 Efl.Object.constructor; 11 Efl.Object.constructor;
12 Efl.Object.destructor;
12 } 13 }
13} 14}