eo-cxx: race for promises

This commit is contained in:
Felipe Magno de Almeida 2016-08-15 14:47:16 -03:00
parent dbed78ad3b
commit 2bdad3f1d6
3 changed files with 730 additions and 19 deletions

View File

@ -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

View File

@ -13,24 +13,118 @@
#include <mutex>
#include <condition_variable>
#include <eina_tuple.hh>
namespace efl {
template <typename...Args>
struct shared_future;
namespace _impl {
template <typename F, typename...Args>
struct future_invoke_result_of;
template <typename...Futures>
struct all_result_type;
template <typename F, typename A0>
struct future_invoke_result_of<F, A0>
template <typename...Args>
struct all_result_type<shared_future<Args...>>
{
typedef typename std::result_of<F(A0)>::type type;
typedef shared_future<Args...> type;
};
template <typename F>
struct future_invoke_result_of<F, void>
template <typename...Args1, typename...Args2>
struct all_result_type<shared_future<Args1...>, shared_future<Args2...>>
{
typedef typename std::result_of<F()>::type type;
typedef shared_future<Args1..., Args2...> type;
};
template <typename...Args1, typename...Args2, typename...OtherFutures>
struct all_result_type<shared_future<Args1...>, shared_future<Args2...>, OtherFutures...>
{
typedef typename all_result_type<shared_future<Args1..., Args2...>, OtherFutures...>::type type;
};
template <typename...Futures>
typename all_result_type<Futures...>::type
all_impl(Futures const& ... futures)
{
Efl_Future* future = ::efl_future_all_internal(futures.native_handle()..., NULL);
return typename all_result_type<Futures...>::type{ ::efl_ref(future)};
}
template <typename...Futures>
struct race_result_type;
template <typename...Args>
struct race_result_type<shared_future<Args...>>
{
typedef shared_future<Args...> type;
};
template <typename T, typename...Args>
struct race_compose_impl;
template <typename T, typename A0, typename...Args>
struct race_compose_impl<T, A0, Args...>
{
typedef typename std::conditional<eina::_mpl::tuple_contains<A0, T>::value
, typename race_compose_impl<T, Args...>::type
, typename race_compose_impl<typename eina::_mpl::push_back<T, A0>::type, Args...>::type
>::type type;
};
template <typename T>
struct race_compose_impl<T>
{
typedef T type;
};
template <typename T>
struct variant_from_tuple;
template <typename...Args>
struct variant_from_tuple<std::tuple<Args...>>
{
typedef eina::variant<Args...> type;
};
template <typename...Args>
struct race_variant
{
typedef typename variant_from_tuple<typename race_compose_impl<std::tuple<>, Args...>::type>::type type;
};
template <typename A0>
struct race_result_type<shared_future<A0>>
{
typedef shared_future<A0> type;
};
template <typename A0>
struct race_result_type<shared_future<eina::variant<A0>>>
{
typedef shared_future<A0> type;
};
template <typename...Args1, typename...Args2>
struct race_result_type<shared_future<Args1...>, shared_future<Args2...>>
{
typedef typename race_result_type<shared_future<typename race_variant<Args1..., Args2...>::type>>::type type;
};
template <typename...Args1, typename...Args2, typename...OtherFutures>
struct race_result_type<shared_future<Args1...>, shared_future<Args2...>, OtherFutures...>
{
typedef typename race_result_type<shared_future<typename race_variant<Args1..., Args2...>::type>
, OtherFutures...>::type type;
};
template <typename...Futures>
typename race_result_type<Futures...>::type
race_impl(Futures const& ... futures)
{
Efl_Future* future = ::efl_future_race_internal(futures.native_handle()..., NULL);
return typename race_result_type<Futures...>::type{ ::efl_ref(future)};
}
template <typename A0, typename F>
typename std::enable_if
@ -70,14 +164,74 @@ future_invoke(F f, Efl_Event const* event)
{
}
}
template <std::size_t N, typename...Args, typename...StorageArgs>
static void future_invoke_impl_read_accessor
(Eina_Accessor*, std::tuple<StorageArgs...>&, std::tuple<Args...>*, std::true_type)
{
}
template <std::size_t N, typename...Args, typename...StorageArgs>
static void future_invoke_impl_read_accessor
(Eina_Accessor* accessor
, std::tuple<StorageArgs...>& storage_tuple
, std::tuple<Args...>* args
, std::false_type)
{
typedef std::tuple<Args...> tuple_type;
typedef typename std::tuple_element<N, tuple_type>::type type;
void* value;
if(eina_accessor_data_get(accessor, N, &value))
{
eina::copy_from_c_traits<type>::copy_to_unitialized
(static_cast<type*>(static_cast<void*>(&std::get<N>(storage_tuple))), value);
_impl::future_invoke_impl_read_accessor<N+1>
(accessor, storage_tuple, args
, std::integral_constant<bool, (N+1 == sizeof...(Args))>());
}
else
{
std::abort();
// some error
}
}
template <typename F, typename...Args, std::size_t...I>
void future_invoke_impl(F f, Efl_Event const* event, std::tuple<Args...>* arguments_dummy, eina::index_sequence<I...>)
{
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
try
{
typedef std::tuple<Args...> arguments;
typedef std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...>
storage_tuple_type;
storage_tuple_type storage_tuple;
future_invoke_impl_read_accessor<0ul>
(static_cast<Eina_Accessor*>(info->value)
, storage_tuple
, arguments_dummy, std::false_type{});
auto r = f(*static_cast<typename std::tuple_element<I, arguments>::type*>
(static_cast<void*>(&std::get<I>(storage_tuple)))...);
typedef decltype(r) result_type;
typedef typename eina::alloc_to_c_traits<result_type>::c_type c_type;
c_type* c_value = eina::alloc_to_c_traits<result_type>::copy_alloc(r);
efl_promise_value_set(info->next, c_value, & eina::alloc_to_c_traits<result_type>::free_alloc);
}
catch(...)
{
}
}
template <typename A0, typename A1, typename...OtherArgs, typename F>
// typename future_invoke_result_of<F, A0, A1, OtherArgs...>::type
void
future_invoke(F f, Efl_Event const* event)
{
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
std::tuple<A0, A1, OtherArgs...>* p = nullptr;
_impl::future_invoke_impl(f, event, p, eina::make_index_sequence<sizeof...(OtherArgs) + 2>{});
}
}
@ -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<type>::copy_to_unitialized
(static_cast<type*>(static_cast<void*>(&std::get<N>(storage_tuple))), value);
std::cout << "copied value " << *static_cast<type*>(static_cast<void*>(&std::get<N>(storage_tuple)))
<< std::endl;
_self_type::read_accessor<N+1>(accessor, storage_tuple, wait_state
, std::integral_constant<bool, (N+1 == sizeof...(Args))>());
}
@ -405,6 +554,20 @@ void then(shared_future<Args...> future, F function)
}
template <typename...Args1, typename...Args2, typename...Futures>
typename _impl::all_result_type<shared_future<Args1...>, shared_future<Args2...>, Futures...>::type
all(shared_future<Args1...> future1, shared_future<Args2...> future2, Futures...futures)
{
return _impl::all_impl(future1, future2, futures...);
}
template <typename...Args1, typename...Args2, typename...Futures>
typename _impl::race_result_type<shared_future<Args1...>, shared_future<Args2...>, Futures...>::type
race(shared_future<Args1...> future1, shared_future<Args2...> future2, Futures...futures)
{
return _impl::race_impl(future1, future2, futures...);
}
template <typename...Args>
struct promise
{

View File

@ -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<int, int> future(efl_ref(f3));
efl::shared_future<int> 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<int*>(malloc(sizeof(int)));
*i1 = 5;
efl_promise_value_set(p1, i1, &::free);
int* i2 = static_cast<int*>(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<int> future1(efl_ref(f1))
, future2(efl_ref(f2));
efl::shared_future<int, int> 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<int> future1(efl_ref(f1))
, future2(efl_ref(f2));
efl::shared_future<int, int> 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<int*>(malloc(sizeof(int)));
*i1 = 5;
efl_promise_value_set(p1, i1, & ::free);
int* i2 = static_cast<int*>(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<int> future1(efl_ref(f1))
, future2(efl_ref(f2));
efl::shared_future<int, int> 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<int*>(malloc(sizeof(int)));
*i1 = 5;
efl_promise_value_set(p1, i1, & ::free);
int* i2 = static_cast<int*>(malloc(sizeof(int)));
*i2 = 42;
efl_promise_value_set(p2, i2, & ::free);
});
std::tuple<int, int> 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<int> future1(efl_ref(f1)), future2(efl_ref(f2));
efl::shared_future<int, int> future = all(future1, future2);
efl::shared_future<int> 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<int*>(malloc(sizeof(int)));
*i1 = 5;
efl_promise_value_set(p1, i1, &::free);
int* i2 = static_cast<int*>(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<int> future1(efl_ref(f1)), future2(efl_ref(f2));
efl::shared_future<double> future3(efl_ref(f2));
efl::shared_future<int> race1 = race(future1, future2);
efl::shared_future<efl::eina::variant<int, double>> 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<int> future1(efl_ref(f1))
, future2(efl_ref(f2));
efl::shared_future<int> 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<int*>(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<int> future1(efl_ref(f1))
, future2(efl_ref(f2));
efl::shared_future<int> 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<int*>(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<int> future1(efl_ref(f1)), future2(efl_ref(f2));
efl::shared_future<int> future = race(future1, future2);
efl::shared_future<int> 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<int*>(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<int> future1(efl_ref(f1));
efl::shared_future<double> future2(efl_ref(f2));
efl::shared_future<efl::eina::variant<int, double>> 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<int*>(malloc(sizeof(int)));
*i1 = 5;
efl_promise_value_set(p1, i1, & ::free);
});
efl::eina::variant<int, double> value = future3.get();
ck_assert(efl::eina::get<int>(&value) != nullptr);
ck_assert_int_eq(efl::eina::get<int>(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<int> future1(efl_ref(f1));
efl::shared_future<double> future2(efl_ref(f2));
efl::shared_future<efl::eina::variant<int, double>> future = race(future1, future2);
efl::shared_future<int> rfuture;
std::thread thread
([&]
{
efl::ecore::main_loop_thread_safe_call_sync
([&]
{
rfuture = then
(future, [] (efl::eina::variant<int, double> v) -> int
{
ck_assert(efl::eina::get<int>(&v) != nullptr);
ck_assert_int_eq(efl::eina::get<int>(v), 5);
return 42;
}, [] (std::error_code)
{
throw std::bad_alloc();
});
});
efl::ecore::main_loop_thread_safe_call_async
([&]
{
int* i1 = static_cast<int*>(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);
}