From 2bdad3f1d66ea3c4b38409eaa8bfa72245dc428b Mon Sep 17 00:00:00 2001 From: Felipe Magno de Almeida Date: Mon, 15 Aug 2016 14:47:16 -0300 Subject: [PATCH] eo-cxx: race for promises --- src/Makefile_Eolian_Cxx.am | 3 +- src/bindings/cxx/eo_cxx/eo_promise.hh | 197 ++++++++- src/tests/eo_cxx/eo_cxx_test_promise.cc | 549 ++++++++++++++++++++++++ 3 files changed, 730 insertions(+), 19 deletions(-) diff --git a/src/Makefile_Eolian_Cxx.am b/src/Makefile_Eolian_Cxx.am index c142a4b5fa..71a930ef11 100644 --- a/src/Makefile_Eolian_Cxx.am +++ b/src/Makefile_Eolian_Cxx.am @@ -45,8 +45,7 @@ lib/eolian_cxx/grammar/sequence.hpp \ lib/eolian_cxx/grammar/string.hpp \ lib/eolian_cxx/grammar/type.hpp \ lib/eolian_cxx/grammar/type_impl.hpp \ -lib/eolian_cxx/grammar/type_traits.hpp \ -lib/eolian_cxx/grammar/variant.hpp +lib/eolian_cxx/grammar/type_traits.hpp ### Binary diff --git a/src/bindings/cxx/eo_cxx/eo_promise.hh b/src/bindings/cxx/eo_cxx/eo_promise.hh index b325d9cd12..f9a32610e8 100644 --- a/src/bindings/cxx/eo_cxx/eo_promise.hh +++ b/src/bindings/cxx/eo_cxx/eo_promise.hh @@ -13,24 +13,118 @@ #include #include +#include + namespace efl { +template +struct shared_future; + namespace _impl { -template -struct future_invoke_result_of; +template +struct all_result_type; -template -struct future_invoke_result_of +template +struct all_result_type> { - typedef typename std::result_of::type type; + typedef shared_future type; }; -template -struct future_invoke_result_of +template +struct all_result_type, shared_future> { - typedef typename std::result_of::type type; + typedef shared_future type; }; + +template +struct all_result_type, shared_future, OtherFutures...> +{ + typedef typename all_result_type, OtherFutures...>::type type; +}; + +template +typename all_result_type::type +all_impl(Futures const& ... futures) +{ + Efl_Future* future = ::efl_future_all_internal(futures.native_handle()..., NULL); + return typename all_result_type::type{ ::efl_ref(future)}; +} + +template +struct race_result_type; + +template +struct race_result_type> +{ + typedef shared_future type; +}; + +template +struct race_compose_impl; + +template +struct race_compose_impl +{ + typedef typename std::conditional::value + , typename race_compose_impl::type + , typename race_compose_impl::type, Args...>::type + >::type type; +}; + +template +struct race_compose_impl +{ + typedef T type; +}; + +template +struct variant_from_tuple; + +template +struct variant_from_tuple> +{ + typedef eina::variant type; +}; + +template +struct race_variant +{ + typedef typename variant_from_tuple, Args...>::type>::type type; +}; + +template +struct race_result_type> +{ + typedef shared_future type; +}; + +template +struct race_result_type>> +{ + typedef shared_future type; +}; + +template +struct race_result_type, shared_future> +{ + typedef typename race_result_type::type>>::type type; +}; + +template +struct race_result_type, shared_future, OtherFutures...> +{ + typedef typename race_result_type::type> + , OtherFutures...>::type type; +}; + +template +typename race_result_type::type +race_impl(Futures const& ... futures) +{ + Efl_Future* future = ::efl_future_race_internal(futures.native_handle()..., NULL); + return typename race_result_type::type{ ::efl_ref(future)}; +} template typename std::enable_if @@ -70,14 +164,74 @@ future_invoke(F f, Efl_Event const* event) { } } + +template +static void future_invoke_impl_read_accessor + (Eina_Accessor*, std::tuple&, std::tuple*, std::true_type) +{ +} + +template +static void future_invoke_impl_read_accessor + (Eina_Accessor* accessor + , std::tuple& storage_tuple + , std::tuple* args + , std::false_type) +{ + typedef std::tuple tuple_type; + typedef typename std::tuple_element::type type; + void* value; + if(eina_accessor_data_get(accessor, N, &value)) + { + eina::copy_from_c_traits::copy_to_unitialized + (static_cast(static_cast(&std::get(storage_tuple))), value); + + _impl::future_invoke_impl_read_accessor + (accessor, storage_tuple, args + , std::integral_constant()); + } + else + { + std::abort(); + // some error + } +} + +template +void future_invoke_impl(F f, Efl_Event const* event, std::tuple* arguments_dummy, eina::index_sequence) +{ + Efl_Future_Event_Success* info = static_cast(event->info); + try + { + typedef std::tuple arguments; + typedef std::tuple::type...> + storage_tuple_type; + storage_tuple_type storage_tuple; + + future_invoke_impl_read_accessor<0ul> + (static_cast(info->value) + , storage_tuple + , arguments_dummy, std::false_type{}); + + auto r = f(*static_cast::type*> + (static_cast(&std::get(storage_tuple)))...); + + typedef decltype(r) result_type; + typedef typename eina::alloc_to_c_traits::c_type c_type; + c_type* c_value = eina::alloc_to_c_traits::copy_alloc(r); + efl_promise_value_set(info->next, c_value, & eina::alloc_to_c_traits::free_alloc); + } + catch(...) + { + } +} template -// typename future_invoke_result_of::type void future_invoke(F f, Efl_Event const* event) { - Efl_Future_Event_Success* info = static_cast(event->info); - + std::tuple* p = nullptr; + _impl::future_invoke_impl(f, event, p, eina::make_index_sequence{}); } } @@ -174,9 +328,7 @@ struct shared_future_common } typedef Efl_Future* native_handle_type; - typedef Efl_Future const* const_native_handle_type; - native_handle_type native_handle() noexcept { return _future; } - const_native_handle_type native_handle() const noexcept { return _future; } + native_handle_type native_handle() const noexcept { return _future; } typedef shared_future_common _self_type; Efl_Future* _future; @@ -282,9 +434,6 @@ struct shared_future_varargs_type : private shared_future_common eina::copy_from_c_traits::copy_to_unitialized (static_cast(static_cast(&std::get(storage_tuple))), value); - std::cout << "copied value " << *static_cast(static_cast(&std::get(storage_tuple))) - << std::endl; - _self_type::read_accessor(accessor, storage_tuple, wait_state , std::integral_constant()); } @@ -405,6 +554,20 @@ void then(shared_future future, F function) } +template +typename _impl::all_result_type, shared_future, Futures...>::type +all(shared_future future1, shared_future future2, Futures...futures) +{ + return _impl::all_impl(future1, future2, futures...); +} + +template +typename _impl::race_result_type, shared_future, Futures...>::type +race(shared_future future1, shared_future future2, Futures...futures) +{ + return _impl::race_impl(future1, future2, futures...); +} + template struct promise { diff --git a/src/tests/eo_cxx/eo_cxx_test_promise.cc b/src/tests/eo_cxx/eo_cxx_test_promise.cc index b3a2b32b31..151b9376e9 100644 --- a/src/tests/eo_cxx/eo_cxx_test_promise.cc +++ b/src/tests/eo_cxx/eo_cxx_test_promise.cc @@ -333,6 +333,541 @@ START_TEST(eo_cxx_future_composite_get) } END_TEST +START_TEST(eo_cxx_future_composite_then_value) +{ + + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p2); + + Efl_Future *f2 = efl_promise_future_get(p2); + fail_if(!f2); + + Efl_Future *f3 = efl_future_all(f1, f2); + fail_if(!f3); + + efl::shared_future future(efl_ref(f3)); + efl::shared_future rfuture; + + std::thread thread + ([&] + { + efl::ecore::main_loop_thread_safe_call_sync + ([&] + { + rfuture = then + (future, [] (int i1, int i2) -> int + { + ck_assert_int_eq(i1, 5); + ck_assert_int_eq(i2, 42); + return 42; + }, [] (std::error_code) + { + throw std::bad_alloc(); + }); + }); + efl::ecore::main_loop_thread_safe_call_async + ([&] + { + int* i1 = static_cast(malloc(sizeof(int))); + *i1 = 5; + efl_promise_value_set(p1, i1, &::free); + int* i2 = static_cast(malloc(sizeof(int))); + *i2 = 42; + efl_promise_value_set(p2, i2, &::free); + }); + + int i; + i = rfuture.get(); + ck_assert_int_eq(i, 42); + efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); }); + }); + + ecore_main_loop_begin(); + thread.join(); + } + ecore_shutdown(); +} +END_TEST + +START_TEST(eo_cxx_future_all_construct_and_destroy) +{ + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f2 = efl_promise_future_get(p2); + fail_if(!f2); + + Efl_Future *f3 = efl_future_all(f1, f2); + fail_if(!f3); + + efl::shared_future future1(efl_ref(f1)) + , future2(efl_ref(f2)); + efl::shared_future future3 = all(future1, future2); + } + ecore_shutdown(); +} +END_TEST + +START_TEST(eo_cxx_future_all_wait) +{ + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future* f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p2); + + Efl_Future* f2 = efl_promise_future_get(p2); + fail_if(!f2); + + efl::shared_future future1(efl_ref(f1)) + , future2(efl_ref(f2)); + efl::shared_future future3 = all(future1, future2); + + std::thread thread([&] + { + efl::ecore::main_loop_thread_safe_call_sync([]{}); // wait for ecore_main_loop_begin() call to start + efl::ecore::main_loop_thread_safe_call_async + ([&] + { + int* i1 = static_cast(malloc(sizeof(int))); + *i1 = 5; + efl_promise_value_set(p1, i1, & ::free); + int* i2 = static_cast(malloc(sizeof(int))); + *i2 = 42; + efl_promise_value_set(p2, i2, & ::free); + }); + + future3.wait(); + efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); }); + }); + + ecore_main_loop_begin(); + + thread.join(); + } + ecore_shutdown(); +} +END_TEST + +START_TEST(eo_cxx_future_all_get) +{ + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p2); + + Efl_Future *f2 = efl_promise_future_get(p2); + fail_if(!f2); + + efl::shared_future future1(efl_ref(f1)) + , future2(efl_ref(f2)); + efl::shared_future future3 = all(future1, future2); + + std::thread thread([&] + { + efl::ecore::main_loop_thread_safe_call_sync([]{}); // wait for ecore_main_loop_begin() call to start + efl::ecore::main_loop_thread_safe_call_async + ([&] + { + int* i1 = static_cast(malloc(sizeof(int))); + *i1 = 5; + efl_promise_value_set(p1, i1, & ::free); + int* i2 = static_cast(malloc(sizeof(int))); + *i2 = 42; + efl_promise_value_set(p2, i2, & ::free); + }); + + std::tuple tuple = future3.get(); + ck_assert_int_eq(std::get<0>(tuple), 5); + ck_assert_int_eq(std::get<1>(tuple), 42); + efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); }); + }); + + ecore_main_loop_begin(); + + thread.join(); + } + ecore_shutdown(); +} +END_TEST + +START_TEST(eo_cxx_future_all_then_value) +{ + + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p2); + + Efl_Future *f2 = efl_promise_future_get(p2); + fail_if(!f2); + + efl::shared_future future1(efl_ref(f1)), future2(efl_ref(f2)); + efl::shared_future future = all(future1, future2); + efl::shared_future rfuture; + + std::thread thread + ([&] + { + efl::ecore::main_loop_thread_safe_call_sync + ([&] + { + rfuture = then + (future, [] (int i1, int i2) -> int + { + ck_assert_int_eq(i1, 5); + ck_assert_int_eq(i2, 42); + return 42; + }, [] (std::error_code) + { + throw std::bad_alloc(); + }); + }); + efl::ecore::main_loop_thread_safe_call_async + ([&] + { + int* i1 = static_cast(malloc(sizeof(int))); + *i1 = 5; + efl_promise_value_set(p1, i1, &::free); + int* i2 = static_cast(malloc(sizeof(int))); + *i2 = 42; + efl_promise_value_set(p2, i2, &::free); + }); + + int i; + i = rfuture.get(); + ck_assert_int_eq(i, 42); + efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); }); + }); + + ecore_main_loop_begin(); + thread.join(); + } + ecore_shutdown(); +} +END_TEST + +// race +START_TEST(eo_cxx_future_race_construct_and_destroy) +{ + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f2 = efl_promise_future_get(p2); + fail_if(!f2); + + efl::shared_future future1(efl_ref(f1)), future2(efl_ref(f2)); + efl::shared_future future3(efl_ref(f2)); + efl::shared_future race1 = race(future1, future2); + efl::shared_future> race2 = race(future1, future3); + } + ecore_shutdown(); +} +END_TEST + +START_TEST(eo_cxx_future_race_wait) +{ + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future* f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p2); + + Efl_Future* f2 = efl_promise_future_get(p2); + fail_if(!f2); + + efl::shared_future future1(efl_ref(f1)) + , future2(efl_ref(f2)); + efl::shared_future future3 = race(future1, future2); + + std::thread thread([&] + { + efl::ecore::main_loop_thread_safe_call_sync([]{}); // wait for ecore_main_loop_begin() call to start + efl::ecore::main_loop_thread_safe_call_async + ([&] + { + int* i1 = static_cast(malloc(sizeof(int))); + *i1 = 5; + efl_promise_value_set(p1, i1, & ::free); + }); + + future3.wait(); + efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); }); + }); + + ecore_main_loop_begin(); + + thread.join(); + } + ecore_shutdown(); +} +END_TEST + +START_TEST(eo_cxx_future_race_get) +{ + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p2); + + Efl_Future *f2 = efl_promise_future_get(p2); + fail_if(!f2); + + efl::shared_future future1(efl_ref(f1)) + , future2(efl_ref(f2)); + efl::shared_future future3 = race(future1, future2); + + std::thread thread([&] + { + efl::ecore::main_loop_thread_safe_call_sync([]{}); // wait for ecore_main_loop_begin() call to start + efl::ecore::main_loop_thread_safe_call_async + ([&] + { + int* i1 = static_cast(malloc(sizeof(int))); + *i1 = 5; + efl_promise_value_set(p1, i1, & ::free); + }); + + int value = future3.get(); + ck_assert_int_eq(value, 5); + efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); }); + }); + + ecore_main_loop_begin(); + + thread.join(); + } + ecore_shutdown(); +} +END_TEST + +START_TEST(eo_cxx_future_race_then_value) +{ + + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p2); + + Efl_Future *f2 = efl_promise_future_get(p2); + fail_if(!f2); + + efl::shared_future future1(efl_ref(f1)), future2(efl_ref(f2)); + efl::shared_future future = race(future1, future2); + efl::shared_future rfuture; + + std::thread thread + ([&] + { + efl::ecore::main_loop_thread_safe_call_sync + ([&] + { + rfuture = then + (future, [] (int i) -> int + { + ck_assert_int_eq(i, 5); + return 42; + }, [] (std::error_code) + { + throw std::bad_alloc(); + }); + }); + efl::ecore::main_loop_thread_safe_call_async + ([&] + { + int* i1 = static_cast(malloc(sizeof(int))); + *i1 = 5; + efl_promise_value_set(p1, i1, &::free); + }); + + int i; + i = rfuture.get(); + ck_assert_int_eq(i, 42); + efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); }); + }); + + ecore_main_loop_begin(); + thread.join(); + } + ecore_shutdown(); +} +END_TEST + +START_TEST(eo_cxx_future_race_variant_get) +{ + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p2); + + Efl_Future *f2 = efl_promise_future_get(p2); + fail_if(!f2); + + efl::shared_future future1(efl_ref(f1)); + efl::shared_future future2(efl_ref(f2)); + efl::shared_future> future3 = race(future1, future2); + + std::thread thread([&] + { + efl::ecore::main_loop_thread_safe_call_sync([]{}); // wait for ecore_main_loop_begin() call to start + efl::ecore::main_loop_thread_safe_call_async + ([&] + { + int* i1 = static_cast(malloc(sizeof(int))); + *i1 = 5; + efl_promise_value_set(p1, i1, & ::free); + }); + + efl::eina::variant value = future3.get(); + ck_assert(efl::eina::get(&value) != nullptr); + ck_assert_int_eq(efl::eina::get(value), 5); + efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); }); + }); + + ecore_main_loop_begin(); + + thread.join(); + } + ecore_shutdown(); +} +END_TEST + +START_TEST(eo_cxx_future_race_variant_then_value) +{ + + ecore_init(); + + { + Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p1); + + Efl_Future *f1 = efl_promise_future_get(p1); + fail_if(!f1); + + Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get()); + fail_if(!p2); + + Efl_Future *f2 = efl_promise_future_get(p2); + fail_if(!f2); + + efl::shared_future future1(efl_ref(f1)); + efl::shared_future future2(efl_ref(f2)); + efl::shared_future> future = race(future1, future2); + efl::shared_future rfuture; + + std::thread thread + ([&] + { + efl::ecore::main_loop_thread_safe_call_sync + ([&] + { + rfuture = then + (future, [] (efl::eina::variant v) -> int + { + ck_assert(efl::eina::get(&v) != nullptr); + ck_assert_int_eq(efl::eina::get(v), 5); + return 42; + }, [] (std::error_code) + { + throw std::bad_alloc(); + }); + }); + efl::ecore::main_loop_thread_safe_call_async + ([&] + { + int* i1 = static_cast(malloc(sizeof(int))); + *i1 = 5; + efl_promise_value_set(p1, i1, &::free); + }); + + int i; + i = rfuture.get(); + ck_assert_int_eq(i, 42); + efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); }); + }); + + ecore_main_loop_begin(); + thread.join(); + } + ecore_shutdown(); +} +END_TEST + + void eo_cxx_test_promise(TCase* tc) { @@ -344,4 +879,18 @@ eo_cxx_test_promise(TCase* tc) tcase_add_test(tc, eo_cxx_future_composite_construct_and_destroy); tcase_add_test(tc, eo_cxx_future_composite_wait); tcase_add_test(tc, eo_cxx_future_composite_get); + tcase_add_test(tc, eo_cxx_future_composite_then_value); + + tcase_add_test(tc, eo_cxx_future_all_construct_and_destroy); + tcase_add_test(tc, eo_cxx_future_all_wait); + tcase_add_test(tc, eo_cxx_future_all_get); + tcase_add_test(tc, eo_cxx_future_all_then_value); + + tcase_add_test(tc, eo_cxx_future_race_construct_and_destroy); + tcase_add_test(tc, eo_cxx_future_race_wait); + tcase_add_test(tc, eo_cxx_future_race_get); + tcase_add_test(tc, eo_cxx_future_race_then_value); + + tcase_add_test(tc, eo_cxx_future_race_variant_get); + tcase_add_test(tc, eo_cxx_future_race_variant_then_value); }