eo-cxx: race for promises
authorFelipe Magno de Almeida <felipe@expertisesolutions.com.br>
Mon, 15 Aug 2016 17:47:16 +0000 (14:47 -0300)
committerFelipe Magno de Almeida <felipe@expertisesolutions.com.br>
Wed, 14 Sep 2016 03:33:22 +0000 (00:33 -0300)
src/Makefile_Eolian_Cxx.am
src/bindings/cxx/eo_cxx/eo_promise.hh
src/tests/eo_cxx/eo_cxx_test_promise.cc

index c142a4b..71a930e 100644 (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
 
index b325d9c..f9a3261 100644 (file)
 #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...Args>
+struct all_result_type<shared_future<Args...>>
+{
+  typedef shared_future<Args...> type;
+};
+
+template <typename...Args1, typename...Args2>
+struct all_result_type<shared_future<Args1...>, shared_future<Args2...>>
+{
+  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 F, typename A0>
-struct future_invoke_result_of<F, A0>
+template <typename...Futures>
+typename all_result_type<Futures...>::type
+all_impl(Futures const& ... futures)
 {
-   typedef typename std::result_of<F(A0)>::type type;
+  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 F>
-struct future_invoke_result_of<F, void>
+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::result_of<F()>::type type;
+  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
 {
index b3a2b32..151b937 100644 (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);
 }