diff options
author | Lauro Moura <lauromoura@expertisesolutions.com.br> | 2019-12-03 08:52:14 -0300 |
---|---|---|
committer | Lauro Moura <lauromoura@expertisesolutions.com.br> | 2019-12-16 18:48:52 -0300 |
commit | 9b3ed6ace5dd92d37994b736acbacfdb1baeb5bf (patch) | |
tree | 7749c83ee2e4cedbb0d81df3d47550c2a5dbbc28 | |
parent | d2f849a7a4dcfceefe4413f3e82c05355c706664 (diff) |
WIP csharp: Fix passing acessor with ownershipdevs/lauromoura/T8486-csharp-collections
When passing an owned acessor from a converted collection we need a way
to unpin the passed data when the accessor is freed.
This commits adds a thin wrapper around the CArray accessor that unpins
the data when freed.
NOTE: Needs to update the test after D10878 commented out the accessor
indexer
-rw-r--r-- | src/bindings/mono/efl_mono/meson.build | 1 | ||||
-rw-r--r-- | src/bindings/mono/eina_mono/eina_accessor.cs | 6 | ||||
-rw-r--r-- | src/bindings/mono/eo_mono/iwrapper.cs | 39 | ||||
-rw-r--r-- | src/lib/efl_mono/efl_mono_accessors.c | 93 | ||||
-rw-r--r-- | src/tests/efl_mono/Eo.cs | 36 | ||||
-rw-r--r-- | src/tests/efl_mono/dummy_test_object.c | 17 | ||||
-rw-r--r-- | src/tests/efl_mono/dummy_test_object.eo | 7 |
7 files changed, 185 insertions, 14 deletions
diff --git a/src/bindings/mono/efl_mono/meson.build b/src/bindings/mono/efl_mono/meson.build index e93d323747..165b6d55bd 100644 --- a/src/bindings/mono/efl_mono/meson.build +++ b/src/bindings/mono/efl_mono/meson.build | |||
@@ -83,6 +83,7 @@ endforeach | |||
83 | efl_mono_lib = library('eflcustomexportsmono', | 83 | efl_mono_lib = library('eflcustomexportsmono', |
84 | [ | 84 | [ |
85 | join_paths('..', '..', '..', 'lib', 'efl_mono', 'efl_custom_exports_mono.c'), | 85 | join_paths('..', '..', '..', 'lib', 'efl_mono', 'efl_custom_exports_mono.c'), |
86 | join_paths('..', '..', '..', 'lib', 'efl_mono', 'efl_mono_accessors.c'), | ||
86 | join_paths('..', '..', '..', 'lib', 'efl_mono', 'efl_mono_model_internal.c'), | 87 | join_paths('..', '..', '..', 'lib', 'efl_mono', 'efl_mono_model_internal.c'), |
87 | ], | 88 | ], |
88 | pub_eo_file_target, | 89 | pub_eo_file_target, |
diff --git a/src/bindings/mono/eina_mono/eina_accessor.cs b/src/bindings/mono/eina_mono/eina_accessor.cs index 7c8b52c561..50e9f691fc 100644 --- a/src/bindings/mono/eina_mono/eina_accessor.cs +++ b/src/bindings/mono/eina_mono/eina_accessor.cs | |||
@@ -34,6 +34,12 @@ internal class AccessorNativeFunctions | |||
34 | eina_accessor_data_get(IntPtr accessor, uint position, out IntPtr data); | 34 | eina_accessor_data_get(IntPtr accessor, uint position, out IntPtr data); |
35 | [DllImport(efl.Libs.Eina)] public static extern void | 35 | [DllImport(efl.Libs.Eina)] public static extern void |
36 | eina_accessor_free(IntPtr accessor); | 36 | eina_accessor_free(IntPtr accessor); |
37 | [DllImport(efl.Libs.CustomExports)] public static extern IntPtr | ||
38 | eina_mono_owned_carray_length_accessor_new(IntPtr array, | ||
39 | uint step, | ||
40 | uint length, | ||
41 | Eina.Callbacks.EinaFreeCb freeCb, | ||
42 | IntPtr handle); | ||
37 | } | 43 | } |
38 | 44 | ||
39 | /// <summary>Accessors provide an uniform way of accessing Eina containers, | 45 | /// <summary>Accessors provide an uniform way of accessing Eina containers, |
diff --git a/src/bindings/mono/eo_mono/iwrapper.cs b/src/bindings/mono/eo_mono/iwrapper.cs index e761935892..421ce189fe 100644 --- a/src/bindings/mono/eo_mono/iwrapper.cs +++ b/src/bindings/mono/eo_mono/iwrapper.cs | |||
@@ -790,21 +790,44 @@ public static class Globals | |||
790 | return wrappedAccessor.Handle; | 790 | return wrappedAccessor.Handle; |
791 | } | 791 | } |
792 | 792 | ||
793 | var list = new List<IntPtr>(); | 793 | // TODO: Check if we're either an Eina.List or Eina.Collection? |
794 | // We could just rewrap their native accessors | ||
795 | IntPtr[] intPtrs = new IntPtr[enumerable.Count()]; | ||
796 | |||
797 | int i = 0; | ||
794 | foreach (T data in enumerable) | 798 | foreach (T data in enumerable) |
795 | { | 799 | { |
796 | list.Add(Eina.TraitFunctions.ManagedToNativeAlloc<T>(data)); | 800 | intPtrs[i] = Eina.TraitFunctions.ManagedToNativeAlloc<T>(data); |
801 | i++; | ||
797 | } | 802 | } |
798 | IntPtr[] dataArray = list.ToArray(); | ||
799 | GCHandle pinnedArray = GCHandle.Alloc(dataArray, GCHandleType.Pinned); //FIXME: Need to free. | ||
800 | IntPtr ret = Eina.AccessorNativeFunctions.eina_carray_length_accessor_new(pinnedArray.AddrOfPinnedObject(), (uint)(IntPtr.Size), (uint)dataArray.Length); | ||
801 | 803 | ||
802 | if (!isMoved) | 804 | IntPtr[] dataArray = intPtrs.ToArray(); |
805 | GCHandle pinnedArray = GCHandle.Alloc(dataArray, GCHandleType.Pinned); | ||
806 | |||
807 | IntPtr nativeAccessor = IntPtr.Zero; | ||
808 | |||
809 | if (isMoved) | ||
803 | { | 810 | { |
804 | // FIXME Need to free ret and unpin pinnedArray in the future. | 811 | // We need a custom accessor that would unpin the data when freed. |
812 | nativeAccessor = Eina.AccessorNativeFunctions.eina_mono_owned_carray_length_accessor_new(pinnedArray.AddrOfPinnedObject(), | ||
813 | (uint)IntPtr.Size, | ||
814 | (uint)dataArray.Length, | ||
815 | free_gchandle, | ||
816 | GCHandle.ToIntPtr(pinnedArray)); | ||
817 | } | ||
818 | else | ||
819 | { | ||
820 | // FIXME: Leaking.... | ||
821 | nativeAccessor = Eina.AccessorNativeFunctions.eina_carray_length_accessor_new(pinnedArray.AddrOfPinnedObject(), (uint)(IntPtr.Size), (uint)dataArray.Length); | ||
805 | } | 822 | } |
806 | 823 | ||
807 | return ret; | 824 | if (nativeAccessor == IntPtr.Zero) |
825 | { | ||
826 | pinnedArray.Free(); | ||
827 | throw new InvalidOperationException("Failed to get native accessor for the given container"); | ||
828 | } | ||
829 | |||
830 | return nativeAccessor; | ||
808 | } | 831 | } |
809 | 832 | ||
810 | internal static IEnumerable<T> IteratorToIEnumerable<T>(IntPtr iterator) | 833 | internal static IEnumerable<T> IteratorToIEnumerable<T>(IntPtr iterator) |
diff --git a/src/lib/efl_mono/efl_mono_accessors.c b/src/lib/efl_mono/efl_mono_accessors.c new file mode 100644 index 0000000000..6482045c87 --- /dev/null +++ b/src/lib/efl_mono/efl_mono_accessors.c | |||
@@ -0,0 +1,93 @@ | |||
1 | /* | ||
2 | * Copyright 2019 by its authors. See AUTHORS. | ||
3 | * | ||
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | * you may not use this file except in compliance with the License. | ||
6 | * You may obtain a copy of the License at | ||
7 | * | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | * | ||
10 | * Unless required by applicable law or agreed to in writing, software | ||
11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | * See the License for the specific language governing permissions and | ||
14 | * limitations under the License. | ||
15 | */ | ||
16 | |||
17 | |||
18 | |||
19 | #include "Eina.h" | ||
20 | |||
21 | #ifdef EAPI | ||
22 | # undef EAPI | ||
23 | #endif | ||
24 | |||
25 | #ifdef _WIN32 | ||
26 | # define EAPI __declspec(dllexport) | ||
27 | #else | ||
28 | # ifdef __GNUC__ | ||
29 | # if __GNUC__ >= 4 | ||
30 | # define EAPI __attribute__ ((visibility("default"))) | ||
31 | # else | ||
32 | # define EAPI | ||
33 | # endif | ||
34 | # else | ||
35 | # define EAPI | ||
36 | # endif | ||
37 | #endif /* ! _WIN32 */ | ||
38 | |||
39 | // This just a wrapper around carray acessors for pinned managed data | ||
40 | // It uses the free callback to unpin the managed data so it can be | ||
41 | // reclaimed by the GC back in C# world. | ||
42 | struct _Eina_Mono_Owned_Accessor | ||
43 | { | ||
44 | Eina_Accessor accessor; | ||
45 | |||
46 | Eina_Accessor *carray_acc; | ||
47 | void *free_data; | ||
48 | Eina_Free_Cb free_cb; | ||
49 | }; | ||
50 | |||
51 | typedef struct _Eina_Mono_Owned_Accessor Eina_Mono_Owned_Accessor; | ||
52 | |||
53 | static Eina_Bool eina_mono_owned_carray_get_at(Eina_Mono_Owned_Accessor *accessor, unsigned int idx, void **data) | ||
54 | { | ||
55 | return eina_accessor_data_get(accessor->carray_acc, idx, data); | ||
56 | } | ||
57 | |||
58 | static void** eina_mono_owned_carray_get_container(Eina_Mono_Owned_Accessor *accessor) | ||
59 | { | ||
60 | // Is another accessor a valid container? | ||
61 | return (void**)&accessor->carray_acc; | ||
62 | } | ||
63 | |||
64 | static void eina_mono_owned_carray_free_cb(Eina_Mono_Owned_Accessor* accessor) | ||
65 | { | ||
66 | accessor->free_cb(accessor->free_data); | ||
67 | |||
68 | free(accessor->carray_acc); // From Eina_CArray_Length_Accessor implementation... | ||
69 | |||
70 | free(accessor); | ||
71 | } | ||
72 | |||
73 | EAPI Eina_Accessor *eina_mono_owned_carray_length_accessor_new(void** array, unsigned int step, unsigned int length, Eina_Free_Cb free_cb, void *handle) | ||
74 | { | ||
75 | Eina_Mono_Owned_Accessor *accessor = calloc(1, sizeof(Eina_Mono_Owned_Accessor)); | ||
76 | if (!accessor) return NULL; | ||
77 | |||
78 | EINA_MAGIC_SET(&accessor->accessor, EINA_MAGIC_ACCESSOR); | ||
79 | |||
80 | accessor->carray_acc = eina_carray_length_accessor_new(array, step, length); | ||
81 | |||
82 | accessor->accessor.version = EINA_ACCESSOR_VERSION; | ||
83 | accessor->accessor.get_at = FUNC_ACCESSOR_GET_AT(eina_mono_owned_carray_get_at); | ||
84 | accessor->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER(eina_mono_owned_carray_get_container); | ||
85 | accessor->accessor.free = FUNC_ACCESSOR_FREE(eina_mono_owned_carray_free_cb); | ||
86 | |||
87 | // The managed callback to be called with the pinned data. | ||
88 | accessor->free_cb = free_cb; | ||
89 | // The managed pinned data to be unpinned. | ||
90 | accessor->free_data = handle; | ||
91 | |||
92 | return &accessor->accessor; | ||
93 | } \ No newline at end of file | ||
diff --git a/src/tests/efl_mono/Eo.cs b/src/tests/efl_mono/Eo.cs index 586a18cba3..6f50d8f018 100644 --- a/src/tests/efl_mono/Eo.cs +++ b/src/tests/efl_mono/Eo.cs | |||
@@ -257,18 +257,21 @@ class TestVariables | |||
257 | 257 | ||
258 | class TestEoAccessors | 258 | class TestEoAccessors |
259 | { | 259 | { |
260 | private static void do_eo_accessors(IEnumerable<int> accessor) | 260 | private static void do_eo_accessors(IEnumerable<int> accessor, bool shouldMove=false) |
261 | { | 261 | { |
262 | var obj = new Dummy.TestObject(); | 262 | var obj = new Dummy.TestObject(); |
263 | 263 | ||
264 | IEnumerable<int> acc = obj.CloneAccessor(accessor); | 264 | IEnumerable<int> source = shouldMove ? accessor.ToList() : accessor; |
265 | 265 | ||
266 | var zipped = acc.Zip(accessor, (first, second) => new Tuple<int, int>(first, second)); | 266 | IEnumerable<int> acc = shouldMove ? obj.CloneAccessorOwn(accessor) : obj.CloneAccessor(accessor); |
267 | |||
268 | var zipped = acc.Zip(source, (first, second) => new Tuple<int, int>(first, second)); | ||
267 | 269 | ||
268 | foreach (Tuple<int, int> pair in zipped) | 270 | foreach (Tuple<int, int> pair in zipped) |
269 | { | 271 | { |
270 | Test.AssertEquals(pair.Item1, pair.Item2); | 272 | Test.AssertEquals(pair.Item1, pair.Item2); |
271 | } | 273 | } |
274 | |||
272 | obj.Dispose(); | 275 | obj.Dispose(); |
273 | } | 276 | } |
274 | 277 | ||
@@ -280,16 +283,26 @@ class TestEoAccessors | |||
280 | lst.Append(2); | 283 | lst.Append(2); |
281 | lst.Append(5); | 284 | lst.Append(5); |
282 | 285 | ||
283 | // FIXME: Replace the first accessor with the list once Eina.List implements Eina.IList | ||
284 | do_eo_accessors(lst.GetAccessor()); | 286 | do_eo_accessors(lst.GetAccessor()); |
285 | 287 | ||
286 | lst.Dispose(); | 288 | lst.Dispose(); |
287 | } | 289 | } |
290 | public static void eina_eo_accessors_own() | ||
291 | { | ||
292 | Eina.List<int> lst = new Eina.List<int>(); | ||
293 | lst.Append(4); | ||
294 | lst.Append(3); | ||
295 | lst.Append(2); | ||
296 | lst.Append(1); | ||
297 | Eina.Accessor<int> acc = lst.GetAccessor(); | ||
298 | do_eo_accessors(acc, shouldMove : true); | ||
299 | |||
300 | Test.Assert(!acc.Own); | ||
301 | |||
302 | } | ||
288 | 303 | ||
289 | public static void managed_eo_accessors() | 304 | public static void managed_eo_accessors() |
290 | { | 305 | { |
291 | var obj = new Dummy.TestObject(); | ||
292 | |||
293 | List<int> lst = new List<int>(); | 306 | List<int> lst = new List<int>(); |
294 | lst.Add(-1); | 307 | lst.Add(-1); |
295 | lst.Add(1); | 308 | lst.Add(1); |
@@ -298,6 +311,17 @@ class TestEoAccessors | |||
298 | 311 | ||
299 | do_eo_accessors(lst); | 312 | do_eo_accessors(lst); |
300 | } | 313 | } |
314 | |||
315 | public static void managed_eo_accessors_own() | ||
316 | { | ||
317 | List<int> lst = new List<int>(); | ||
318 | lst.Add(-1); | ||
319 | lst.Add(1); | ||
320 | lst.Add(4); | ||
321 | lst.Add(42); | ||
322 | |||
323 | do_eo_accessors(lst, shouldMove : true); | ||
324 | } | ||
301 | } | 325 | } |
302 | 326 | ||
303 | class TestEoFinalize | 327 | class TestEoFinalize |
diff --git a/src/tests/efl_mono/dummy_test_object.c b/src/tests/efl_mono/dummy_test_object.c index fb87a8c1bd..d9ab519d0a 100644 --- a/src/tests/efl_mono/dummy_test_object.c +++ b/src/tests/efl_mono/dummy_test_object.c | |||
@@ -4683,6 +4683,23 @@ Eina_Accessor *_dummy_test_object_clone_accessor(Eo *obj EINA_UNUSED, Dummy_Test | |||
4683 | return eina_list_accessor_new(pd->list_for_accessor); | 4683 | return eina_list_accessor_new(pd->list_for_accessor); |
4684 | } | 4684 | } |
4685 | 4685 | ||
4686 | Eina_Accessor *_dummy_test_object_clone_accessor_own(Eo *obj EINA_UNUSED, Dummy_Test_Object_Data *pd, Eina_Accessor *acc) | ||
4687 | { | ||
4688 | if (pd->list_for_accessor) | ||
4689 | eina_list_free(pd->list_for_accessor); | ||
4690 | |||
4691 | unsigned int i; | ||
4692 | int *data; | ||
4693 | EINA_ACCESSOR_FOREACH(acc, i, data) | ||
4694 | { | ||
4695 | pd->list_for_accessor = eina_list_append(pd->list_for_accessor, data); | ||
4696 | } | ||
4697 | |||
4698 | eina_accessor_free(acc); | ||
4699 | |||
4700 | return eina_list_accessor_new(pd->list_for_accessor); | ||
4701 | } | ||
4702 | |||
4686 | void _dummy_test_object_dummy_test_iface_emit_nonconflicted(Eo *obj, Dummy_Test_Object_Data *pd EINA_UNUSED) | 4703 | void _dummy_test_object_dummy_test_iface_emit_nonconflicted(Eo *obj, Dummy_Test_Object_Data *pd EINA_UNUSED) |
4687 | { | 4704 | { |
4688 | efl_event_callback_legacy_call(obj, DUMMY_TEST_IFACE_EVENT_NONCONFLICTED, NULL); | 4705 | efl_event_callback_legacy_call(obj, DUMMY_TEST_IFACE_EVENT_NONCONFLICTED, NULL); |
diff --git a/src/tests/efl_mono/dummy_test_object.eo b/src/tests/efl_mono/dummy_test_object.eo index 1852160623..cf2ae7ce03 100644 --- a/src/tests/efl_mono/dummy_test_object.eo +++ b/src/tests/efl_mono/dummy_test_object.eo | |||
@@ -1621,6 +1621,13 @@ class Dummy.Test_Object extends Efl.Object implements Dummy.Test_Iface { | |||
1621 | return: accessor<int> @move; | 1621 | return: accessor<int> @move; |
1622 | } | 1622 | } |
1623 | 1623 | ||
1624 | clone_accessor_own { | ||
1625 | params { | ||
1626 | @in acc: accessor<int> @move; | ||
1627 | } | ||
1628 | return: accessor<int> @move; | ||
1629 | } | ||
1630 | |||
1624 | @property setter_only { | 1631 | @property setter_only { |
1625 | set {} | 1632 | set {} |
1626 | values { | 1633 | values { |