efl_mono: Start generating eina future in eolian_mono.
Summary: Besides the normal methods returning Futures, we now generate a wrapper with the "Async" suffix. This wrapper returns a Systems.Threading.Tasks.Task which can be awaited on and reflect the status of the Future. When an eina.Future fails with ECANCELED, TaskCanceledException is raised in the Task. Otherwise, an efl.FutureException(eina.Error) is raised. Depends on D6174 Reviewers: felipealmeida Reviewed By: felipealmeida Subscribers: cedric, zmike Tags: #efl Differential Revision: https://phab.enlightenment.org/D6175
This commit is contained in:
parent
fff0c86d99
commit
c8fbc31ee2
|
@ -68,6 +68,7 @@ bin_eolian_mono_eolian_mono_SOURCES = \
|
|||
bin/eolian_mono/eolian/mono/documentation.hh \
|
||||
bin/eolian_mono/eolian/mono/type.hh \
|
||||
bin/eolian_mono/eolian/mono/marshall_annotation.hh \
|
||||
bin/eolian_mono/eolian/mono/async_function_definition.hh \
|
||||
bin/eolian_mono/eolian/mono/function_pointer.hh \
|
||||
bin/eolian_mono/eolian/mono/function_definition.hh \
|
||||
bin/eolian_mono/eolian/mono/name_helpers.hh \
|
||||
|
@ -434,6 +435,7 @@ tests_efl_mono_efl_mono_SOURCES = \
|
|||
tests/efl_mono/Eina.cs \
|
||||
tests/efl_mono/Eldbus.cs \
|
||||
tests/efl_mono/Eo.cs \
|
||||
tests/efl_mono/EoPromises.cs \
|
||||
tests/efl_mono/Errors.cs \
|
||||
tests/efl_mono/Evas.cs \
|
||||
tests/efl_mono/Events.cs \
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
#ifndef EOLIAN_MONO_ASYNC_FUNCTION_DEFINITION_HH
|
||||
#define EOLIAN_MONO_ASYNC_FUNCTION_DEFINITION_HH
|
||||
|
||||
#include <Eina.hh>
|
||||
|
||||
#include "grammar/generator.hpp"
|
||||
#include "grammar/klass_def.hpp"
|
||||
|
||||
#include "grammar/indentation.hpp"
|
||||
#include "grammar/list.hpp"
|
||||
#include "grammar/alternative.hpp"
|
||||
#include "grammar/attribute_reorder.hpp"
|
||||
#include "logging.hh"
|
||||
#include "type.hh"
|
||||
#include "name_helpers.hh"
|
||||
#include "helpers.hh"
|
||||
#include "function_helpers.hh"
|
||||
#include "marshall_type.hh"
|
||||
#include "parameter.hh"
|
||||
#include "documentation.hh"
|
||||
#include "using_decl.hh"
|
||||
#include "generation_contexts.hh"
|
||||
#include "blacklist.hh"
|
||||
|
||||
namespace eolian_mono {
|
||||
|
||||
struct is_future
|
||||
{
|
||||
typedef is_future visitor_type;
|
||||
typedef bool result_type;
|
||||
|
||||
bool operator()(grammar::attributes::complex_type_def const& c) const
|
||||
{
|
||||
return c.outer.base_type == "future";
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator()(T const&) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct async_function_declaration_generator
|
||||
{
|
||||
template<typename OutputIterator, typename Context>
|
||||
bool generate(OutputIterator sink, attributes::function_def const& f, Context const& context) const
|
||||
{
|
||||
if (f.is_static)
|
||||
return true;
|
||||
if (blacklist::is_function_blacklisted(f.c_name))
|
||||
return true;
|
||||
if (!f.return_type.original_type.visit(is_future{}))
|
||||
return true;
|
||||
|
||||
if (!as_generator(
|
||||
scope_tab << "System.Threading.Tasks.Task<eina.Value> " << name_helpers::managed_async_method_name(f) << "(" << *(parameter << ",") <<
|
||||
" System.Threading.CancellationToken token=default(System.Threading.CancellationToken));\n"
|
||||
).generate(sink, f.parameters, context))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
} const async_function_declaration {};
|
||||
|
||||
struct async_function_definition_generator
|
||||
{
|
||||
async_function_definition_generator(bool do_super = false)
|
||||
: do_super(do_super)
|
||||
{}
|
||||
|
||||
template <typename OutputIterator, typename Context>
|
||||
bool generate(OutputIterator sink, attributes::function_def const& f, Context const& context) const
|
||||
{
|
||||
EINA_CXX_DOM_LOG_DBG(eolian_mono::domain) << "async_function_definition_generator: " << f.c_name;
|
||||
|
||||
if(do_super && f.is_static) // Static methods goes only on Concrete classes.
|
||||
return true;
|
||||
if(blacklist::is_function_blacklisted(f.c_name))
|
||||
return true;
|
||||
if(!f.return_type.original_type.visit(is_future{}))
|
||||
return true;
|
||||
|
||||
auto parameter_forwarding = [](attributes::parameter_def const& param) {
|
||||
return direction_modifier(param) + " " + name_helpers::escape_keyword(param.param_name);
|
||||
};
|
||||
std::vector<std::string> param_forwarding;
|
||||
|
||||
std::transform(f.parameters.begin(), f.parameters.end(), std::back_inserter(param_forwarding), parameter_forwarding);
|
||||
|
||||
if(!as_generator(
|
||||
scope_tab << "public System.Threading.Tasks.Task<eina.Value> " << name_helpers::managed_async_method_name(f) << "(" << *(parameter << ",") << " System.Threading.CancellationToken token)\n"
|
||||
<< scope_tab << "{\n"
|
||||
<< scope_tab << scope_tab << "eina.Future future = " << name_helpers::managed_method_name(f) << "(" << (string % ",") << ");\n"
|
||||
<< scope_tab << scope_tab << "return efl.eo.Globals.WrapAsync(future, token);\n"
|
||||
<< scope_tab << "}\n"
|
||||
).generate(sink, std::make_tuple(f.parameters, param_forwarding), context))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool do_super;
|
||||
};
|
||||
|
||||
struct async_function_definition_parameterized
|
||||
{
|
||||
async_function_definition_generator operator()(bool do_super=false) const
|
||||
{
|
||||
return {do_super};
|
||||
}
|
||||
} const async_function_definition;
|
||||
async_function_definition_generator as_generator(async_function_definition_parameterized)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace efl { namespace eolian { namespace grammar {
|
||||
|
||||
template <>
|
||||
struct is_eager_generator< ::eolian_mono::async_function_declaration_generator> : std::true_type {};
|
||||
template <>
|
||||
struct is_generator< ::eolian_mono::async_function_declaration_generator> : std::true_type {};
|
||||
|
||||
template <>
|
||||
struct is_eager_generator< ::eolian_mono::async_function_definition_generator> : std::true_type {};
|
||||
template <>
|
||||
struct is_generator< ::eolian_mono::async_function_definition_generator> : std::true_type {};
|
||||
template <>
|
||||
struct is_generator< ::eolian_mono::async_function_definition_parameterized> : std::true_type {};
|
||||
|
||||
namespace type_traits {
|
||||
template <>
|
||||
struct attributes_needed< ::eolian_mono::async_function_declaration_generator> : std::integral_constant<int, 1> {};
|
||||
|
||||
template <>
|
||||
struct attributes_needed< ::eolian_mono::async_function_definition_generator> : std::integral_constant<int, 1> {};
|
||||
template <>
|
||||
struct attributes_needed< ::eolian_mono::async_function_definition_parameterized> : std::integral_constant<int, 1> {};
|
||||
}
|
||||
|
||||
} } }
|
||||
|
||||
#endif
|
|
@ -11,6 +11,7 @@
|
|||
#include "grammar/alternative.hpp"
|
||||
#include "type.hh"
|
||||
#include "name_helpers.hh"
|
||||
#include "async_function_definition.hh"
|
||||
#include "function_definition.hh"
|
||||
#include "function_registration.hh"
|
||||
#include "function_declaration.hh"
|
||||
|
@ -144,6 +145,9 @@ struct klass
|
|||
if(!as_generator(*(scope_tab << function_declaration)).generate(sink, cls.functions, iface_cxt))
|
||||
return false;
|
||||
|
||||
if(!as_generator(*(scope_tab << async_function_declaration)).generate(sink, cls.functions, iface_cxt))
|
||||
return false;
|
||||
|
||||
if(!as_generator(*(event_declaration)).generate(sink, cls.events, iface_cxt))
|
||||
return false;
|
||||
|
||||
|
@ -251,6 +255,10 @@ struct klass
|
|||
if(!as_generator(*(function_definition))
|
||||
.generate(sink, methods, concrete_cxt)) return false;
|
||||
|
||||
// Async wrappers
|
||||
if(!as_generator(*(async_function_definition)).generate(sink, methods, concrete_cxt))
|
||||
return false;
|
||||
|
||||
if(!as_generator(*(event_argument_wrapper)).generate(sink, cls.events, context))
|
||||
return false;
|
||||
|
||||
|
@ -354,6 +362,10 @@ struct klass
|
|||
if(!as_generator(*(function_definition(true)))
|
||||
.generate(sink, methods, inherit_cxt)) return false;
|
||||
|
||||
// Async wrappers
|
||||
if(!as_generator(*(async_function_definition(true))).generate(sink, methods, inherit_cxt))
|
||||
return false;
|
||||
|
||||
if(!as_generator("}\n").generate(sink, attributes::unused, inherit_cxt)) return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -149,8 +149,13 @@ struct marshall_annotation_visitor_generate
|
|||
<< string << ", efl.eo." << (klass_name.base_qualifier & qualifier_info::is_own ? "OwnTag" : "NonOwnTag") << ">))]"
|
||||
).generate(sink, name_helpers::klass_full_concrete_name(klass_name), *context);
|
||||
}
|
||||
bool operator()(attributes::complex_type_def const&) const
|
||||
bool operator()(attributes::complex_type_def const& c) const
|
||||
{
|
||||
if (c.outer.base_type == "future")
|
||||
{
|
||||
std::string prefix = is_return ? "return: " : "";
|
||||
return as_generator("[" << prefix << "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(eina.FutureMarshaler))]").generate(sink, nullptr, *context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -254,8 +259,13 @@ struct marshall_native_annotation_visitor_generate
|
|||
<< string << ", efl.eo." << (klass_name.base_qualifier & qualifier_info::is_own ? "OwnTag" : "NonOwnTag") << ">))]"
|
||||
).generate(sink, name_helpers::klass_full_concrete_name(klass_name), *context);
|
||||
}
|
||||
bool operator()(attributes::complex_type_def const&) const
|
||||
bool operator()(attributes::complex_type_def const& c) const
|
||||
{
|
||||
if (c.outer.base_type == "future")
|
||||
{
|
||||
std::string prefix = is_return ? "return: " : "";
|
||||
return as_generator("[" << prefix << "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(eina.FutureMarshaler))]").generate(sink, nullptr, *context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -192,6 +192,10 @@ inline std::string alias_full_eolian_name(attributes::alias_def const& alias)
|
|||
return join_namespaces(alias.namespaces, '.') + alias.eolian_name;
|
||||
}
|
||||
|
||||
inline std::string managed_async_method_name(attributes::function_def const& f)
|
||||
{
|
||||
return managed_method_name(f) + "Async";
|
||||
}
|
||||
inline std::string function_ptr_full_eolian_name(attributes::function_def const& func)
|
||||
{
|
||||
return join_namespaces(func.namespaces, '.') + func.name;
|
||||
|
|
|
@ -282,16 +282,10 @@ struct visitor_generate
|
|||
complex_type_def c = complex;
|
||||
c.outer.base_type = "eina.Hash";
|
||||
return c;
|
||||
}}
|
||||
, {"promise", nullptr, nullptr, [&]
|
||||
{
|
||||
return replace_outer
|
||||
(complex, regular_type_def{" ::efl::promise", complex.outer.base_qualifier, {}});
|
||||
}
|
||||
}
|
||||
}}
|
||||
, {"future", nullptr, nullptr, [&]
|
||||
{
|
||||
(*this)(regular_type_def{" int", complex.outer.base_qualifier, {}});
|
||||
(*this)(regular_type_def{" eina.Future", complex.outer.base_qualifier, {}});
|
||||
return attributes::type_def::variant_type();
|
||||
// return replace_outer
|
||||
// (complex, regular_type_def{" ::efl::shared_future", complex.outer.base_qualifier, {}});
|
||||
|
|
|
@ -168,7 +168,7 @@ public class Future
|
|||
/// </summary>
|
||||
public delegate eina.Value ResolvedCb(eina.Value value);
|
||||
|
||||
private IntPtr Handle;
|
||||
public IntPtr Handle { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Future from a native pointer.
|
||||
|
@ -295,4 +295,40 @@ public class Future
|
|||
}
|
||||
}
|
||||
|
||||
public class FutureMarshaler : ICustomMarshaler
|
||||
{
|
||||
|
||||
public object MarshalNativeToManaged(IntPtr pNativeData)
|
||||
{
|
||||
return new Future(pNativeData);
|
||||
}
|
||||
|
||||
public IntPtr MarshalManagedToNative(object managedObj)
|
||||
{
|
||||
Future f = managedObj as Future;
|
||||
if (f == null)
|
||||
return IntPtr.Zero;
|
||||
return f.Handle;
|
||||
}
|
||||
|
||||
public void CleanUpNativeData(IntPtr pNativeData) { }
|
||||
|
||||
public void CleanUpManagedData(object managedObj) { }
|
||||
|
||||
public int GetNativeDataSize()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static ICustomMarshaler GetInstance(string cookie) {
|
||||
if (marshaler == null)
|
||||
{
|
||||
marshaler = new FutureMarshaler();
|
||||
}
|
||||
return marshaler;
|
||||
}
|
||||
|
||||
private static FutureMarshaler marshaler;
|
||||
}
|
||||
|
||||
} // namespace eina
|
||||
|
|
|
@ -4,6 +4,7 @@ using System;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
using static eina.NativeCustomExportFunctions;
|
||||
|
||||
|
@ -179,7 +180,51 @@ public class Globals {
|
|||
GCHandle handle = GCHandle.FromIntPtr(ptr);
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public static System.Threading.Tasks.Task<eina.Value> WrapAsync(eina.Future future, CancellationToken token)
|
||||
{
|
||||
// Creates a task that will wait for SetResult for completion.
|
||||
// TaskCompletionSource is used to create tasks for 'external' Task sources.
|
||||
var tcs = new System.Threading.Tasks.TaskCompletionSource<eina.Value>();
|
||||
|
||||
// Flag to be passed to the cancell callback
|
||||
bool fulfilled = false;
|
||||
|
||||
future.Then((eina.Value received) => {
|
||||
lock (future)
|
||||
{
|
||||
// Convert an failed Future to a failed Task.
|
||||
if (received.GetValueType() == eina.ValueType.Error)
|
||||
{
|
||||
eina.Error err;
|
||||
received.Get(out err);
|
||||
if (err == eina.Error.ECANCELED)
|
||||
tcs.SetCanceled();
|
||||
else
|
||||
tcs.TrySetException(new efl.FutureException(received));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Will mark the returned task below as completed.
|
||||
tcs.SetResult(received);
|
||||
}
|
||||
fulfilled = true;
|
||||
return received;
|
||||
}
|
||||
});
|
||||
// Callback to be called when the token is cancelled.
|
||||
token.Register(() => {
|
||||
lock (future)
|
||||
{
|
||||
// Will trigger the Then callback above with an eina.Error
|
||||
if (!fulfilled)
|
||||
future.Cancel();
|
||||
}
|
||||
});
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
} // Globals
|
||||
|
||||
public static class Config
|
||||
{
|
||||
|
@ -449,11 +494,30 @@ public class StrbufKeepOwnershipMarshaler: ICustomMarshaler {
|
|||
|
||||
} // namespace eo
|
||||
|
||||
/// <summary>General exception for errors inside the binding.</summary>
|
||||
public class EflException : Exception
|
||||
{
|
||||
/// <summary>Create a new EflException with the given.
|
||||
public EflException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Exception to be raised when a Task fails due to a failed eina.Future.</summary>
|
||||
public class FutureException : EflException
|
||||
{
|
||||
/// <summary>The error code returned by the failed eina.Future.</summary>
|
||||
public eina.Error Error { get; private set; }
|
||||
|
||||
/// <summary>Construct a new exception from the eina.Error stored in the given eina.Value.</summary>
|
||||
public FutureException(eina.Value value) : base("Future failed.")
|
||||
{
|
||||
if (value.GetValueType() != eina.ValueType.Error)
|
||||
throw new ArgumentException("FutureException must receive an eina.Value with eina.Error.");
|
||||
eina.Error err;
|
||||
value.Get(out err);
|
||||
Error = err;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace efl
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
|
||||
namespace TestSuite
|
||||
{
|
||||
|
||||
class TestEoPromises
|
||||
{
|
||||
public static void test_simple_task_run()
|
||||
{
|
||||
efl.ILoop loop = efl.App.GetLoopMain();
|
||||
eina.Future future = loop.Idle();
|
||||
|
||||
bool callbackCalled = false;
|
||||
int ret_code = 1992;
|
||||
|
||||
future.Then((eina.Value value) => {
|
||||
callbackCalled = true;
|
||||
eina.Value v = new eina.Value(eina.ValueType.Int32);
|
||||
v.Set(ret_code);
|
||||
loop.Quit(v);
|
||||
return value;
|
||||
});
|
||||
eina.Value ret_value = loop.Begin();
|
||||
|
||||
Test.Assert(callbackCalled, "Future loop callback must have been called.");
|
||||
|
||||
Test.AssertEquals(ret_value.GetValueType(), eina.ValueType.Int32);
|
||||
|
||||
int ret_from_value;
|
||||
Test.Assert(ret_value.Get(out ret_from_value));
|
||||
Test.AssertEquals(ret_from_value, ret_code);
|
||||
|
||||
}
|
||||
|
||||
public static void test_object_promise()
|
||||
{
|
||||
efl.ILoop loop = efl.App.GetLoopMain();
|
||||
test.Testing obj = new test.Testing();
|
||||
|
||||
eina.Future future = obj.GetFuture();
|
||||
|
||||
bool callbackCalled = false;
|
||||
int receivedValue = -1;
|
||||
int sentValue = 1984;
|
||||
future.Then((eina.Value value) => {
|
||||
callbackCalled = true;
|
||||
Test.AssertEquals(value.GetValueType(), eina.ValueType.Int32);
|
||||
value.Get(out receivedValue);
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
obj.FulfillPromise(sentValue);
|
||||
|
||||
loop.Iterate();
|
||||
Test.Assert(callbackCalled, "Future callback must have been called.");
|
||||
Test.AssertEquals(receivedValue, sentValue);
|
||||
}
|
||||
|
||||
public static void test_object_promise_cancel()
|
||||
{
|
||||
efl.ILoop loop = efl.App.GetLoopMain();
|
||||
test.Testing obj = new test.Testing();
|
||||
|
||||
eina.Future future = obj.GetFuture();
|
||||
|
||||
bool callbackCalled = false;
|
||||
eina.Error receivedError = -1;
|
||||
eina.Error sentError = 120;
|
||||
future.Then((eina.Value value) => {
|
||||
callbackCalled = true;
|
||||
Test.AssertEquals(value.GetValueType(), eina.ValueType.Error);
|
||||
value.Get(out receivedError);
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
obj.RejectPromise(sentError);
|
||||
|
||||
loop.Iterate();
|
||||
Test.Assert(callbackCalled, "Future callback must have been called.");
|
||||
Test.AssertEquals(receivedError, sentError);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class LoopConsumer
|
||||
{
|
||||
public static async Task Consume(efl.ILoop loop)
|
||||
{
|
||||
Task<eina.Value> task = loop.IdleAsync();
|
||||
eina.Value v = await task;
|
||||
loop.Quit(v);
|
||||
}
|
||||
}
|
||||
|
||||
class TestLoopEoAsyncMethods
|
||||
{
|
||||
public static void test_simple_async()
|
||||
{
|
||||
efl.ILoop loop = efl.App.GetLoopMain();
|
||||
Task t = LoopConsumer.Consume(loop);
|
||||
|
||||
loop.Begin();
|
||||
Test.Assert(t.Wait(1000), "Task should have been completed in time.");
|
||||
}
|
||||
}
|
||||
|
||||
class TestEoAsyncMethods
|
||||
{
|
||||
|
||||
public static void test_async_fulfill()
|
||||
{
|
||||
efl.ILoop loop = efl.App.GetLoopMain();
|
||||
test.ITesting obj = new test.Testing();
|
||||
|
||||
Task<eina.Value> task = obj.GetFutureAsync();
|
||||
|
||||
int sentValue = 1337;
|
||||
|
||||
obj.FulfillPromise(sentValue);
|
||||
loop.Iterate();
|
||||
|
||||
eina.Value v = task.Result;
|
||||
Test.AssertEquals(v.GetValueType(), eina.ValueType.Int32);
|
||||
|
||||
int receivedValue;
|
||||
v.Get(out receivedValue);
|
||||
Test.AssertEquals(receivedValue, sentValue);
|
||||
}
|
||||
|
||||
public static void test_async_cancel()
|
||||
{
|
||||
efl.ILoop loop = efl.App.GetLoopMain();
|
||||
test.ITesting obj = new test.Testing();
|
||||
|
||||
CancellationTokenSource cancelSrc = new CancellationTokenSource();
|
||||
Task<eina.Value> task = obj.GetFutureAsync(cancelSrc.Token);
|
||||
|
||||
cancelSrc.Cancel();
|
||||
loop.Iterate();
|
||||
|
||||
bool raised = false;
|
||||
try
|
||||
{
|
||||
eina.Value v = task.Result;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
raised = true;
|
||||
ae.Handle((x) =>
|
||||
{
|
||||
Test.Assert(x is TaskCanceledException, "AggregateException must have been TaskCanceledException");
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
Test.Assert(raised, "AggregateException must have been raised.");
|
||||
}
|
||||
|
||||
public static void test_async_reject()
|
||||
{
|
||||
efl.ILoop loop = efl.App.GetLoopMain();
|
||||
test.ITesting obj = new test.Testing();
|
||||
|
||||
Task<eina.Value> task = obj.GetFutureAsync();
|
||||
|
||||
eina.Error sentError = 1337;
|
||||
obj.RejectPromise(sentError);
|
||||
|
||||
loop.Iterate();
|
||||
|
||||
bool raised = false;
|
||||
try
|
||||
{
|
||||
eina.Value v = task.Result;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
raised = true;
|
||||
ae.Handle((x) =>
|
||||
{
|
||||
Test.Assert(x is efl.FutureException, "AggregateException must have been TaskCanceledException");
|
||||
efl.FutureException ex = x as efl.FutureException;
|
||||
Test.AssertEquals(ex.Error, sentError);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
Test.Assert(raised, "AggregateException must have been raised.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -51,6 +51,7 @@ typedef struct Test_Testing_Data
|
|||
int stored_int;
|
||||
Eo *part1;
|
||||
Eo *part2;
|
||||
Eina_Promise *promise;
|
||||
} Test_Testing_Data;
|
||||
|
||||
typedef struct Test_Numberwrapper_Data
|
||||
|
@ -3799,6 +3800,48 @@ void _test_testing_klass_prop_set(Eo *klass, EINA_UNUSED void *pd, int value)
|
|||
_test_testing_klass_prop = value;
|
||||
}
|
||||
|
||||
static void _promise_cancelled(void *data, EINA_UNUSED const Eina_Promise *p)
|
||||
{
|
||||
Test_Testing_Data *pd = data;
|
||||
pd->promise = NULL;
|
||||
}
|
||||
|
||||
Eina_Future* _test_testing_get_future(EINA_UNUSED Eo *obj, Test_Testing_Data *pd)
|
||||
{
|
||||
if (pd->promise == NULL)
|
||||
{
|
||||
Eo *loop = efl_app_loop_main_get(EFL_APP_CLASS);
|
||||
Eina_Future_Scheduler *scheduler = efl_loop_future_scheduler_get(loop);
|
||||
pd->promise = eina_promise_new(scheduler, _promise_cancelled, pd);
|
||||
}
|
||||
return eina_future_new(pd->promise);
|
||||
}
|
||||
|
||||
void _test_testing_fulfill_promise(Eo *obj, Test_Testing_Data *pd, int data)
|
||||
{
|
||||
if (pd->promise == NULL)
|
||||
{
|
||||
EINA_LOG_ERR("Can't fulfill an object without a valid promise.");
|
||||
return;
|
||||
}
|
||||
Eina_Value v;
|
||||
eina_value_setup(&v, EINA_VALUE_TYPE_INT);
|
||||
eina_value_set(&v, data);
|
||||
eina_promise_resolve(pd->promise, v);
|
||||
}
|
||||
|
||||
void _test_testing_reject_promise(Eo *obj, Test_Testing_Data *pd, Eina_Error err)
|
||||
{
|
||||
if (pd->promise == NULL)
|
||||
{
|
||||
EINA_LOG_ERR("Can't fulfill an object without a valid promise.");
|
||||
return;
|
||||
}
|
||||
|
||||
eina_promise_reject(pd->promise, err);
|
||||
}
|
||||
|
||||
|
||||
#include "test_testing.eo.c"
|
||||
#include "test_numberwrapper.eo.c"
|
||||
|
||||
|
|
|
@ -1615,6 +1615,24 @@ class Test.Testing (Efl.Object, Efl.Part) {
|
|||
prop: int;
|
||||
}
|
||||
}
|
||||
|
||||
/* Futures */
|
||||
|
||||
get_future {
|
||||
return: future<any_value_ptr>;
|
||||
}
|
||||
|
||||
fulfill_promise {
|
||||
params {
|
||||
@in data: int;
|
||||
}
|
||||
}
|
||||
|
||||
reject_promise {
|
||||
params {
|
||||
@in error: Eina.Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
implements {
|
||||
class.constructor;
|
||||
|
|
Loading…
Reference in New Issue