/// /// @file eo_concrete.hh /// #ifndef EFL_CXX_EO_PROMISE_HH #define EFL_CXX_EO_PROMISE_HH #include #include #include #include #include #include namespace efl { template struct shared_future; namespace _impl { template struct all_result_type; template struct all_result_type> { typedef shared_future type; }; template struct all_result_type, shared_future> { 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 struct future_copy_traits { static void copy(T* storage, Efl_Future_Event_Success const* info) { eina::copy_from_c_traits::copy_to_unitialized (storage, info->value); } }; template struct future_copy_traits> { template static void copy_impl(eina::variant*, void const*, int, std::integral_constant , std::integral_constant) { std::abort(); } template static void copy_impl(eina::variant* storage, void const* value, int index, std::integral_constant , std::integral_constant max , typename std::enable_if::type* = 0) { if(I == index) { eina::copy_from_c_traits>::copy_to_unitialized (storage, static_cast>::type const*> (static_cast(value))); } else copy_impl(storage, value, index, std::integral_constant{}, max); } static void copy(eina::variant* storage, Efl_Future_Event_Success const* other_info) { Efl_Future_Race_Success const* info = static_cast (static_cast(other_info)); copy_impl(storage, info->value, info->index, std::integral_constant{} , std::integral_constant{}); } }; template typename std::enable_if < !std::is_same::value && !std::is_same::type, void>::value >::type future_invoke(F f, Efl_Event const* event) { Efl_Future_Event_Success* info = static_cast(event->info); try { typename std::aligned_storage::type storage; future_copy_traits::copy(static_cast(static_cast(&storage)), info); auto r = f(*static_cast(static_cast(&storage))); 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 std::enable_if::value>::type future_invoke(F f, Efl_Event const* event) { Efl_Future_Event_Success* info = static_cast(event->info); try { f(); } catch(...) { } } 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 void future_invoke(F f, Efl_Event const* event) { std::tuple* p = nullptr; _impl::future_invoke_impl(f, event, p, eina::make_index_sequence{}); } } template struct progress; namespace _impl { template struct wait_state { bool available = false; bool has_failed = false; std::mutex mutex; std::condition_variable cv; typename std::aligned_storage::type storage; Eina_Error error; }; static void get_error_cb(void* data, Efl_Event const* event) { struct wait_state<>* wait_state = static_cast*>(data); Efl_Future_Event_Failure* info = static_cast(event->info); std::unique_lock l(wait_state->mutex); wait_state->error = info->error; wait_state->has_failed = true; wait_state->available = true; wait_state->cv.notify_one(); } struct shared_future_common { explicit shared_future_common(Efl_Future* future) : _future(future) {} shared_future_common() : _future(nullptr) {} ~shared_future_common() { if(_future) efl_unref(_future); } shared_future_common(shared_future_common const& future) : _future(efl_ref(future._future)) { } shared_future_common& operator=(shared_future_common const& other) { _self_type tmp(other); tmp.swap(*this); return *this; } shared_future_common(shared_future_common&& future) : _future(future._future) { future._future = nullptr; } shared_future_common& operator=(shared_future_common&& other) { other.swap(*this); return *this; } void swap(shared_future_common& other) { std::swap(_future, other._future); } bool valid() const noexcept { return _future != nullptr; } void wait() const { if(eina_main_loop_is()) throw std::runtime_error("Deadlock"); struct wait_state<> wait_state; efl::ecore::main_loop_thread_safe_call_async ([&] { efl_future_then(this->_future, &wait_success, &wait_success, nullptr, &wait_state); }); std::unique_lock lock(wait_state.mutex); while(!wait_state.available) wait_state.cv.wait(lock); } static void wait_success(void* data, Efl_Event const*) { struct wait_state<>* wait_state = static_cast*>(data); std::unique_lock l(wait_state->mutex); wait_state->available = true; wait_state->cv.notify_one(); } typedef Efl_Future* native_handle_type; native_handle_type native_handle() const noexcept { return _future; } typedef shared_future_common _self_type; Efl_Future* _future; }; template struct shared_future_1_type : private shared_future_common { typedef shared_future_common _base_type; using _base_type::_base_type; using _base_type::swap; using _base_type::valid; using _base_type::native_handle; using _base_type::wait; typedef _base_type::native_handle_type native_handle_type; T get() const { if(eina_main_loop_is()) throw std::runtime_error("Deadlock"); struct wait_state wait_state; efl::ecore::main_loop_thread_safe_call_async ([&] { efl_future_then(this->_future, &get_success, &_impl::get_error_cb, nullptr, &wait_state); }); { std::unique_lock lock(wait_state.mutex); while(!wait_state.available) wait_state.cv.wait(lock); } if(wait_state.has_failed) EFL_CXX_THROW(eina::system_error(eina::error_code(wait_state.error, eina::eina_error_category()), "EFL Eina Error")); return *static_cast(static_cast(&wait_state.storage)); } static void get_success(void* data, Efl_Event const* event) { struct wait_state* wait_state = static_cast*>(data); Efl_Future_Event_Success* info = static_cast(event->info); std::unique_lock l(wait_state->mutex); _impl::future_copy_traits::copy(static_cast(static_cast(&wait_state->storage)), info); wait_state->available = true; wait_state->cv.notify_one(); } typedef shared_future_1_type _self_type; }; template struct shared_future_varargs_type : private shared_future_common { typedef shared_future_common _base_type; using _base_type::_base_type; using _base_type::swap; using _base_type::valid; using _base_type::native_handle; using _base_type::wait; typedef _base_type::native_handle_type native_handle_type; typedef std::tuple tuple_type; std::tuple get() const { if(eina_main_loop_is()) throw std::runtime_error("Deadlock"); struct wait_state wait_state; efl::ecore::main_loop_thread_safe_call_async ([&] { efl_future_then(this->_future, &get_success, &_impl::get_error_cb, nullptr, &wait_state); }); { std::unique_lock lock(wait_state.mutex); while(!wait_state.available) wait_state.cv.wait(lock); } if(wait_state.has_failed) EFL_CXX_THROW(eina::system_error(eina::error_code(wait_state.error, eina::eina_error_category()), "EFL Eina Error")); return *static_cast(static_cast(&wait_state.storage)); } template static void read_accessor(Eina_Accessor* accessor , std::tuple::type...>& storage_tuple , wait_state* wait_state , std::false_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); _self_type::read_accessor(accessor, storage_tuple, wait_state , std::integral_constant()); } else { std::abort(); // some error } } template static void read_accessor_end(std::tuple::type...>& storage_tuple , wait_state* wait_state , eina::index_sequence) { std::unique_lock l(wait_state->mutex); new (&wait_state->storage) tuple_type{(*static_cast::type*> (static_cast(&std::get(storage_tuple))))...}; wait_state->available = true; wait_state->cv.notify_one(); } template static void read_accessor(Eina_Accessor* , std::tuple::type...>& storage_tuple , wait_state* wait_state , std::true_type) { _self_type::read_accessor_end(storage_tuple, wait_state, eina::make_index_sequence{}); } static void get_success(void* data, Efl_Event const* event) { struct wait_state* wait_state = static_cast*>(data); Efl_Future_Event_Success* info = static_cast(event->info); Eina_Accessor* accessor = static_cast(info->value); std::tuple::type...> storage_tuple; _self_type::read_accessor<0u>(accessor, storage_tuple, wait_state, std::false_type()); } typedef shared_future_varargs_type _self_type; }; } template struct shared_future : private std::conditional>::type>, _impl::shared_future_varargs_type>::type { typedef typename std::conditional>::type>, _impl::shared_future_varargs_type>::type _base_type; using _base_type::_base_type; using _base_type::swap; using _base_type::valid; using _base_type::get; using _base_type::wait; using _base_type::native_handle; typedef typename _base_type::native_handle_type native_handle_type; }; template shared_future < typename std::enable_if < !std::is_same>::type>::value && !std::is_same::type>::value , typename std::result_of::type >::type > then(shared_future future, Success success_cb, Error error_cb) { struct private_data { Success success_cb; Error error_cb; shared_future future; }; private_data* pdata = new private_data {std::move(success_cb), std::move(error_cb), std::move(future)}; Efl_Event_Cb raw_success_cb = [] (void* data, Efl_Event const* event) { private_data* pdata = static_cast(data); try { _impl::future_invoke(pdata->success_cb, event); // should value_set the next promise } catch(...) { // should fail the next promise } delete pdata; }; Efl_Event_Cb raw_error_cb = [] (void* data, Efl_Event const* event) { private_data* pdata = static_cast(data); Efl_Future_Event_Failure* info = static_cast(event->info); pdata->error_cb(eina::error_code(info->error, eina::eina_error_category())); // should error the next promise (or should the promise do that for me automatically?) delete pdata; }; assert(pdata->future.valid()); Efl_Future* new_future = efl_future_then(pdata->future.native_handle(), raw_success_cb, raw_error_cb, nullptr, pdata); return shared_future::type>{efl_ref(new_future)}; } template 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 { }; } #endif