summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLauro Moura <lauromoura@expertisesolutions.com.br>2019-06-27 19:04:59 -0300
committerVitor Sousa <vitorsousa@expertisesolutions.com.br>2019-06-27 19:31:55 -0300
commitef185e5e28d2a92944d4e072d70b21df58bab8e1 (patch)
treec9862c9e6b83e2ab3bbc67bf02b9cedc2cc5d845
parent0f514b484bb3f14ec7c7ef2882f990ff73815527 (diff)
efl-mono: Fix value forwarding in promises/async
Summary: Values returned from C# Then callbacks must release ownership of the underlying native value, so Eina code can clean it up nicely and avoid the Wrapper flushing it early. The same issue applied to the Async wrappers. In this case the value passed as the Task parameter could be released by an `using` block awaiting the value. Also Future creation was then-ing the wrong handle. Also add better exception messages. Reviewers: vitor.sousa, felipealmeida Reviewed By: vitor.sousa Subscribers: cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D9197
-rw-r--r--src/bindings/mono/eina_mono/eina_promises.cs12
-rw-r--r--src/bindings/mono/eina_mono/eina_value.cs24
-rw-r--r--src/bindings/mono/eo_mono/iwrapper.cs6
-rw-r--r--src/tests/efl_mono/Promises.cs25
4 files changed, 57 insertions, 10 deletions
diff --git a/src/bindings/mono/eina_mono/eina_promises.cs b/src/bindings/mono/eina_mono/eina_promises.cs
index 25077a3c97..34561a5b08 100644
--- a/src/bindings/mono/eina_mono/eina_promises.cs
+++ b/src/bindings/mono/eina_mono/eina_promises.cs
@@ -180,6 +180,8 @@ public class Promise : IDisposable
180 { 180 {
181 SanityChecks(); 181 SanityChecks();
182 eina_promise_resolve(this.Handle, value); 182 eina_promise_resolve(this.Handle, value);
183 // Promise will take care of releasing this value correctly.
184 value.ReleaseOwnership();
183 this.Handle = IntPtr.Zero; 185 this.Handle = IntPtr.Zero;
184 // Resolving a cb does *not* call its cancellation callback, so we have to release the 186 // Resolving a cb does *not* call its cancellation callback, so we have to release the
185 // lambda created in the constructor for cleanup. 187 // lambda created in the constructor for cleanup.
@@ -224,11 +226,12 @@ public class Future
224 /// </summary> 226 /// </summary>
225 public Future(IntPtr handle) 227 public Future(IntPtr handle)
226 { 228 {
227 Handle = ThenRaw(handle, (Eina.Value value) => 229 handle = ThenRaw(handle, (Eina.Value value) =>
228 { 230 {
229 Handle = IntPtr.Zero; 231 Handle = IntPtr.Zero;
230 return value; 232 return value;
231 }); 233 });
234 Handle = handle;
232 } 235 }
233 236
234 /// <summary> 237 /// <summary>
@@ -304,7 +307,12 @@ public class Future
304 ResolvedCb cb = handle.Target as ResolvedCb; 307 ResolvedCb cb = handle.Target as ResolvedCb;
305 if (cb != null) 308 if (cb != null)
306 { 309 {
307 value = cb(value); 310 Eina.Value managedValue = cb(value);
311 // Both `value` and `managedValue` will point to the same internal value data.
312 // Avoid C# wrapper invalidating the underlying C Eina_Value as the eina_future.c
313 // code will release it.
314 value = managedValue.GetNative();
315 managedValue.ReleaseOwnership();
308 } 316 }
309 else 317 else
310 { 318 {
diff --git a/src/bindings/mono/eina_mono/eina_value.cs b/src/bindings/mono/eina_mono/eina_value.cs
index 99cf09757d..a19d3d449b 100644
--- a/src/bindings/mono/eina_mono/eina_value.cs
+++ b/src/bindings/mono/eina_mono/eina_value.cs
@@ -47,6 +47,9 @@ static internal class UnsafeNativeMethods
47 internal static extern void eina_value_free(IntPtr type); 47 internal static extern void eina_value_free(IntPtr type);
48 48
49 [DllImport(efl.Libs.Eina)] 49 [DllImport(efl.Libs.Eina)]
50 internal static extern IntPtr eina_value_type_name_get(IntPtr type);
51
52 [DllImport(efl.Libs.Eina)]
50 [return: MarshalAsAttribute(UnmanagedType.U1)] 53 [return: MarshalAsAttribute(UnmanagedType.U1)]
51 internal static extern bool eina_value_convert(IntPtr handle, IntPtr convert); 54 internal static extern bool eina_value_convert(IntPtr handle, IntPtr convert);
52 55
@@ -725,7 +728,16 @@ static class ValueTypeBridge
725 LoadTypes(); 728 LoadTypes();
726 } 729 }
727 730
728 return NativeToManaged[native]; 731 try
732 {
733 return NativeToManaged[native];
734 }
735 catch (KeyNotFoundException ex)
736 {
737 var name_ptr = eina_value_type_name_get(native);
738 var name = Marshal.PtrToStringAnsi(name_ptr);
739 throw new Efl.EflException($"Unknown native eina value Type: 0x{native.ToInt64():x} with name {name}");
740 }
729 } 741 }
730 742
731 public static IntPtr GetNative(ValueType valueType) 743 public static IntPtr GetNative(ValueType valueType)
@@ -978,8 +990,7 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
978 // free(), avoiding a call to eina_value_flush that would wipe the underlying value contents 990 // free(), avoiding a call to eina_value_flush that would wipe the underlying value contents
979 // for pointer types like string. 991 // for pointer types like string.
980 tmp = MemoryNative.Alloc(Marshal.SizeOf(typeof(ValueNative))); 992 tmp = MemoryNative.Alloc(Marshal.SizeOf(typeof(ValueNative)));
981 Marshal.StructureToPtr(value, tmp, false); // Can't get the address of a struct directly. 993 Marshal.StructureToPtr<ValueNative>(value, tmp, false); // Can't get the address of a struct directly.
982 this.Handle = Alloc();
983 994
984 // Copy is used to deep copy the pointed payload (e.g. strings) inside this struct, so we can own this value. 995 // Copy is used to deep copy the pointed payload (e.g. strings) inside this struct, so we can own this value.
985 if (!eina_value_copy(tmp, this.Handle)) 996 if (!eina_value_copy(tmp, this.Handle))
@@ -1482,7 +1493,7 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
1482 { 1493 {
1483 if (Disposed) 1494 if (Disposed)
1484 { 1495 {
1485 throw new ObjectDisposedException(base.GetType().Name); 1496 throw new ObjectDisposedException($"Value at 0x{this.Handle.ToInt64():x}");
1486 } 1497 }
1487 1498
1488 return this.Handle; 1499 return this.Handle;
@@ -1494,7 +1505,7 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
1494 { 1505 {
1495 if (Disposed) 1506 if (Disposed)
1496 { 1507 {
1497 throw new ObjectDisposedException(base.GetType().Name); 1508 throw new ObjectDisposedException($"Value at 0x{this.Handle.ToInt64():x}");
1498 } 1509 }
1499 1510
1500 // Can't call setup with Empty value type (would give an eina error) 1511 // Can't call setup with Empty value type (would give an eina error)
@@ -1540,7 +1551,7 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
1540 { 1551 {
1541 if (Disposed) 1552 if (Disposed)
1542 { 1553 {
1543 throw new ObjectDisposedException(GetType().Name); 1554 throw new ObjectDisposedException($"Value at 0x{this.Handle.ToInt64():x}");
1544 } 1555 }
1545 } 1556 }
1546 1557
@@ -1591,6 +1602,7 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
1591 /// <summary>Get a ValueNative struct with the *value* pointed by this Eina.Value.</summary> 1602 /// <summary>Get a ValueNative struct with the *value* pointed by this Eina.Value.</summary>
1592 public ValueNative GetNative() 1603 public ValueNative GetNative()
1593 { 1604 {
1605 SanityChecks();
1594 ValueNative value = (ValueNative)Marshal.PtrToStructure(this.Handle, typeof(ValueNative)); 1606 ValueNative value = (ValueNative)Marshal.PtrToStructure(this.Handle, typeof(ValueNative));
1595 return value; 1607 return value;
1596 } 1608 }
diff --git a/src/bindings/mono/eo_mono/iwrapper.cs b/src/bindings/mono/eo_mono/iwrapper.cs
index 5fe1daae91..82c08ccf3f 100644
--- a/src/bindings/mono/eo_mono/iwrapper.cs
+++ b/src/bindings/mono/eo_mono/iwrapper.cs
@@ -530,8 +530,10 @@ public class Globals
530 } 530 }
531 else 531 else
532 { 532 {
533 // Will mark the returned task below as completed. 533 // Async receiver methods may consume the value C# wrapper, like when awaiting in the start of an
534 tcs.SetResult(received); 534 // using block. In order to continue to forward the value correctly to the next futures
535 // in the chain, we give the Async wrapper a copy of the received wrapper.
536 tcs.SetResult(new Eina.Value(received));
535 } 537 }
536 538
537 fulfilled = true; 539 fulfilled = true;
diff --git a/src/tests/efl_mono/Promises.cs b/src/tests/efl_mono/Promises.cs
index b355a0ee3d..fc0c089e4e 100644
--- a/src/tests/efl_mono/Promises.cs
+++ b/src/tests/efl_mono/Promises.cs
@@ -42,6 +42,31 @@ class TestPromises
42 Test.AssertEquals(received_value, reference_value); 42 Test.AssertEquals(received_value, reference_value);
43 } 43 }
44 44
45 public static void test_simple_with_object()
46 {
47 bool callbackCalled = false;
48 Eina.Value receivedValue = null;
49
50 Efl.Loop loop = Efl.App.AppMain;
51 Eina.Promise promise = new Eina.Promise();
52 Eina.Future future = new Eina.Future(promise);
53
54 future = future.Then((Eina.Value value) => {
55 callbackCalled = true;
56 receivedValue = new Eina.Value(value);
57 return value;
58 });
59
60 Eina.Value referenceValue = new Eina.Value(Eina.ValueType.Array, Eina.ValueType.Int32);
61 referenceValue.Append(32);
62 promise.Resolve(new Eina.Value(referenceValue));
63
64 loop.Iterate();
65
66 Test.Assert(callbackCalled, "Future callback should have been called.");
67 Test.AssertEquals(receivedValue, referenceValue);
68 }
69
45 public static void test_simple_reject() 70 public static void test_simple_reject()
46 { 71 {
47 bool callbackCalled = false; 72 bool callbackCalled = false;