forked from enlightenment/efl
eo-cxx: Fix race promises
This commit is contained in:
parent
3339f03964
commit
5e735fe762
|
@ -0,0 +1,356 @@
|
||||||
|
///
|
||||||
|
/// @file eo_future.hh
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifndef EFL_CXX_EO_FUTURE_HH
|
||||||
|
#define EFL_CXX_EO_FUTURE_HH
|
||||||
|
|
||||||
|
#include <Efl.h>
|
||||||
|
|
||||||
|
#include <Eina.hh>
|
||||||
|
#include <Ecore_Manual.hh>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include <eina_tuple.hh>
|
||||||
|
#include <eo_promise_meta.hh>
|
||||||
|
|
||||||
|
namespace efl {
|
||||||
|
|
||||||
|
template <typename...Args>
|
||||||
|
struct shared_future;
|
||||||
|
|
||||||
|
namespace _impl {
|
||||||
|
|
||||||
|
template <typename V = char>
|
||||||
|
struct wait_state
|
||||||
|
{
|
||||||
|
bool available = false;
|
||||||
|
bool has_failed = false;
|
||||||
|
std::mutex mutex;
|
||||||
|
std::condition_variable cv;
|
||||||
|
typename std::aligned_storage<sizeof(V), alignof(V)>::type storage;
|
||||||
|
Eina_Error error;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void get_error_cb(void* data, Efl_Event const* event)
|
||||||
|
{
|
||||||
|
struct wait_state<>* wait_state = static_cast<struct wait_state<>*>(data);
|
||||||
|
Efl_Future_Event_Failure* info = static_cast<Efl_Future_Event_Failure*>(event->info);
|
||||||
|
std::unique_lock<std::mutex> 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<std::mutex> 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<struct wait_state<>*>(data);
|
||||||
|
std::unique_lock<std::mutex> 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 <typename T>
|
||||||
|
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<T> 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<std::mutex> 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<T*>(static_cast<void*>(&wait_state.storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_success(void* data, Efl_Event const* event)
|
||||||
|
{
|
||||||
|
struct wait_state<T>* wait_state = static_cast<struct wait_state<T>*>(data);
|
||||||
|
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> l(wait_state->mutex);
|
||||||
|
_impl::future_copy_traits<T>::copy(static_cast<T*>(static_cast<void*>(&wait_state->storage)), info);
|
||||||
|
wait_state->available = true;
|
||||||
|
wait_state->cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef shared_future_1_type<T> _self_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct shared_race_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<T> 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<std::mutex> 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<T*>(static_cast<void*>(&wait_state.storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_success(void* data, Efl_Event const* event)
|
||||||
|
{
|
||||||
|
struct wait_state<T>* wait_state = static_cast<struct wait_state<T>*>(data);
|
||||||
|
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> l(wait_state->mutex);
|
||||||
|
_impl::future_copy_traits<T>::copy_race(static_cast<T*>(static_cast<void*>(&wait_state->storage)), info);
|
||||||
|
wait_state->available = true;
|
||||||
|
wait_state->cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef shared_future_1_type<T> _self_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename...Args>
|
||||||
|
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<Args...> tuple_type;
|
||||||
|
|
||||||
|
std::tuple<Args...> get() const
|
||||||
|
{
|
||||||
|
if(eina_main_loop_is())
|
||||||
|
throw std::runtime_error("Deadlock");
|
||||||
|
|
||||||
|
struct wait_state<tuple_type> 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<std::mutex> 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<tuple_type*>(static_cast<void*>(&wait_state.storage));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
static void read_accessor(Eina_Accessor* accessor
|
||||||
|
, std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...>& storage_tuple
|
||||||
|
, wait_state<tuple_type>* wait_state
|
||||||
|
, std::false_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);
|
||||||
|
|
||||||
|
_self_type::read_accessor<N+1>(accessor, storage_tuple, wait_state
|
||||||
|
, std::integral_constant<bool, (N+1 == sizeof...(Args))>());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::abort();
|
||||||
|
// some error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t N, std::size_t...I>
|
||||||
|
static void read_accessor_end(std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...>& storage_tuple
|
||||||
|
, wait_state<tuple_type>* wait_state
|
||||||
|
, eina::index_sequence<I...>)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> l(wait_state->mutex);
|
||||||
|
|
||||||
|
new (&wait_state->storage) tuple_type{(*static_cast<typename std::tuple_element<I, tuple_type>::type*>
|
||||||
|
(static_cast<void*>(&std::get<I>(storage_tuple))))...};
|
||||||
|
|
||||||
|
wait_state->available = true;
|
||||||
|
wait_state->cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
static void read_accessor(Eina_Accessor*
|
||||||
|
, std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...>& storage_tuple
|
||||||
|
, wait_state<tuple_type>* wait_state
|
||||||
|
, std::true_type)
|
||||||
|
{
|
||||||
|
_self_type::read_accessor_end<N>(storage_tuple, wait_state, eina::make_index_sequence<sizeof...(Args)>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_success(void* data, Efl_Event const* event)
|
||||||
|
{
|
||||||
|
struct wait_state<tuple_type>* wait_state = static_cast<struct wait_state<tuple_type>*>(data);
|
||||||
|
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
||||||
|
|
||||||
|
Eina_Accessor* accessor = static_cast<Eina_Accessor*>(info->value);
|
||||||
|
std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...> storage_tuple;
|
||||||
|
|
||||||
|
_self_type::read_accessor<0u>(accessor, storage_tuple, wait_state, std::false_type());
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef shared_future_varargs_type<Args...> _self_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename...Args>
|
||||||
|
struct shared_future : private std::conditional<sizeof...(Args) == 1, _impl::shared_future_1_type<typename std::tuple_element<0u, std::tuple<Args...>>::type>, _impl::shared_future_varargs_type<Args...>>::type
|
||||||
|
{
|
||||||
|
typedef typename std::conditional<sizeof...(Args) == 1, _impl::shared_future_1_type<typename std::tuple_element<0u, std::tuple<Args...>>::type>, _impl::shared_future_varargs_type<Args...>>::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 <typename...Args>
|
||||||
|
struct shared_race_future : private std::conditional<sizeof...(Args) == 1, _impl::shared_race_future_1_type<typename std::tuple_element<0u, std::tuple<Args...>>::type>, void>::type
|
||||||
|
{
|
||||||
|
typedef typename std::conditional<sizeof...(Args) == 1, _impl::shared_race_future_1_type<typename std::tuple_element<0u, std::tuple<Args...>>::type>, void>::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;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace _impl {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_race_future : std::false_type {};
|
||||||
|
|
||||||
|
template <typename...Args>
|
||||||
|
struct is_race_future<shared_race_future<Args...>> : std::true_type {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include <eina_tuple.hh>
|
#include <eina_tuple.hh>
|
||||||
#include <eo_promise_meta.hh>
|
#include <eo_promise_meta.hh>
|
||||||
|
#include <eo_future.hh>
|
||||||
|
|
||||||
namespace efl {
|
namespace efl {
|
||||||
|
|
||||||
|
@ -24,264 +25,7 @@ struct shared_future;
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct progress;
|
struct progress;
|
||||||
|
|
||||||
namespace _impl {
|
template <template <typename...> class Future, typename...Args, typename Success, typename Error>
|
||||||
|
|
||||||
template <typename V = char>
|
|
||||||
struct wait_state
|
|
||||||
{
|
|
||||||
bool available = false;
|
|
||||||
bool has_failed = false;
|
|
||||||
std::mutex mutex;
|
|
||||||
std::condition_variable cv;
|
|
||||||
typename std::aligned_storage<sizeof(V), alignof(V)>::type storage;
|
|
||||||
Eina_Error error;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void get_error_cb(void* data, Efl_Event const* event)
|
|
||||||
{
|
|
||||||
struct wait_state<>* wait_state = static_cast<struct wait_state<>*>(data);
|
|
||||||
Efl_Future_Event_Failure* info = static_cast<Efl_Future_Event_Failure*>(event->info);
|
|
||||||
std::unique_lock<std::mutex> 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<std::mutex> 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<struct wait_state<>*>(data);
|
|
||||||
std::unique_lock<std::mutex> 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 <typename T>
|
|
||||||
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<T> 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<std::mutex> 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<T*>(static_cast<void*>(&wait_state.storage));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void get_success(void* data, Efl_Event const* event)
|
|
||||||
{
|
|
||||||
struct wait_state<T>* wait_state = static_cast<struct wait_state<T>*>(data);
|
|
||||||
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> l(wait_state->mutex);
|
|
||||||
_impl::future_copy_traits<T>::copy(static_cast<T*>(static_cast<void*>(&wait_state->storage)), info);
|
|
||||||
wait_state->available = true;
|
|
||||||
wait_state->cv.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef shared_future_1_type<T> _self_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename...Args>
|
|
||||||
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<Args...> tuple_type;
|
|
||||||
|
|
||||||
std::tuple<Args...> get() const
|
|
||||||
{
|
|
||||||
if(eina_main_loop_is())
|
|
||||||
throw std::runtime_error("Deadlock");
|
|
||||||
|
|
||||||
struct wait_state<tuple_type> 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<std::mutex> 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<tuple_type*>(static_cast<void*>(&wait_state.storage));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <std::size_t N>
|
|
||||||
static void read_accessor(Eina_Accessor* accessor
|
|
||||||
, std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...>& storage_tuple
|
|
||||||
, wait_state<tuple_type>* wait_state
|
|
||||||
, std::false_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);
|
|
||||||
|
|
||||||
_self_type::read_accessor<N+1>(accessor, storage_tuple, wait_state
|
|
||||||
, std::integral_constant<bool, (N+1 == sizeof...(Args))>());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::abort();
|
|
||||||
// some error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <std::size_t N, std::size_t...I>
|
|
||||||
static void read_accessor_end(std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...>& storage_tuple
|
|
||||||
, wait_state<tuple_type>* wait_state
|
|
||||||
, eina::index_sequence<I...>)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(wait_state->mutex);
|
|
||||||
|
|
||||||
new (&wait_state->storage) tuple_type{(*static_cast<typename std::tuple_element<I, tuple_type>::type*>
|
|
||||||
(static_cast<void*>(&std::get<I>(storage_tuple))))...};
|
|
||||||
|
|
||||||
wait_state->available = true;
|
|
||||||
wait_state->cv.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <std::size_t N>
|
|
||||||
static void read_accessor(Eina_Accessor*
|
|
||||||
, std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...>& storage_tuple
|
|
||||||
, wait_state<tuple_type>* wait_state
|
|
||||||
, std::true_type)
|
|
||||||
{
|
|
||||||
_self_type::read_accessor_end<N>(storage_tuple, wait_state, eina::make_index_sequence<sizeof...(Args)>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
static void get_success(void* data, Efl_Event const* event)
|
|
||||||
{
|
|
||||||
struct wait_state<tuple_type>* wait_state = static_cast<struct wait_state<tuple_type>*>(data);
|
|
||||||
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
|
||||||
|
|
||||||
Eina_Accessor* accessor = static_cast<Eina_Accessor*>(info->value);
|
|
||||||
std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...> storage_tuple;
|
|
||||||
|
|
||||||
_self_type::read_accessor<0u>(accessor, storage_tuple, wait_state, std::false_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef shared_future_varargs_type<Args...> _self_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename...Args>
|
|
||||||
struct shared_future : private std::conditional<sizeof...(Args) == 1, _impl::shared_future_1_type<typename std::tuple_element<0u, std::tuple<Args...>>::type>, _impl::shared_future_varargs_type<Args...>>::type
|
|
||||||
{
|
|
||||||
typedef typename std::conditional<sizeof...(Args) == 1, _impl::shared_future_1_type<typename std::tuple_element<0u, std::tuple<Args...>>::type>, _impl::shared_future_varargs_type<Args...>>::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 <typename...Args, typename Success, typename Error>
|
|
||||||
shared_future
|
shared_future
|
||||||
<
|
<
|
||||||
typename std::enable_if
|
typename std::enable_if
|
||||||
|
@ -290,13 +34,13 @@ shared_future
|
||||||
&& !std::is_same<void, typename std::result_of<Success(Args...)>::type>::value
|
&& !std::is_same<void, typename std::result_of<Success(Args...)>::type>::value
|
||||||
, typename std::result_of<Success(Args...)>::type
|
, typename std::result_of<Success(Args...)>::type
|
||||||
>::type
|
>::type
|
||||||
> then(shared_future<Args...> future, Success success_cb, Error error_cb)
|
> then(Future<Args...> future, Success success_cb, Error error_cb)
|
||||||
{
|
{
|
||||||
struct private_data
|
struct private_data
|
||||||
{
|
{
|
||||||
Success success_cb;
|
Success success_cb;
|
||||||
Error error_cb;
|
Error error_cb;
|
||||||
shared_future<Args...> future;
|
Future<Args...> future;
|
||||||
};
|
};
|
||||||
private_data* pdata = new private_data
|
private_data* pdata = new private_data
|
||||||
{std::move(success_cb), std::move(error_cb), std::move(future)};
|
{std::move(success_cb), std::move(error_cb), std::move(future)};
|
||||||
|
@ -307,7 +51,7 @@ shared_future
|
||||||
private_data* pdata = static_cast<private_data*>(data);
|
private_data* pdata = static_cast<private_data*>(data);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_impl::future_invoke<Args...>(pdata->success_cb, event);
|
_impl::future_invoke<Args...>(pdata->success_cb, event, _impl::is_race_future<Future<Args...>>{});
|
||||||
// should value_set the next promise
|
// should value_set the next promise
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
|
@ -331,7 +75,7 @@ shared_future
|
||||||
= efl_future_then(pdata->future.native_handle(), raw_success_cb, raw_error_cb, nullptr, pdata);
|
= efl_future_then(pdata->future.native_handle(), raw_success_cb, raw_error_cb, nullptr, pdata);
|
||||||
return shared_future<typename std::result_of<Success(Args...)>::type>{efl_ref(new_future)};
|
return shared_future<typename std::result_of<Success(Args...)>::type>{efl_ref(new_future)};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename...Args, typename F>
|
template <typename...Args, typename F>
|
||||||
void then(shared_future<Args...> future, F function)
|
void then(shared_future<Args...> future, F function)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,6 +10,9 @@ namespace efl {
|
||||||
template <typename...Args>
|
template <typename...Args>
|
||||||
struct shared_future;
|
struct shared_future;
|
||||||
|
|
||||||
|
template <typename...Args>
|
||||||
|
struct shared_race_future;
|
||||||
|
|
||||||
namespace _impl {
|
namespace _impl {
|
||||||
|
|
||||||
template <typename...Futures>
|
template <typename...Futures>
|
||||||
|
@ -47,7 +50,7 @@ struct race_result_type;
|
||||||
template <typename...Args>
|
template <typename...Args>
|
||||||
struct race_result_type<shared_future<Args...>>
|
struct race_result_type<shared_future<Args...>>
|
||||||
{
|
{
|
||||||
typedef shared_future<Args...> type;
|
typedef shared_race_future<Args...> type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename...Args>
|
template <typename T, typename...Args>
|
||||||
|
@ -86,13 +89,13 @@ struct race_variant
|
||||||
template <typename A0>
|
template <typename A0>
|
||||||
struct race_result_type<shared_future<A0>>
|
struct race_result_type<shared_future<A0>>
|
||||||
{
|
{
|
||||||
typedef shared_future<A0> type;
|
typedef shared_race_future<A0> type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename A0>
|
template <typename A0>
|
||||||
struct race_result_type<shared_future<eina::variant<A0>>>
|
struct race_result_type<shared_future<eina::variant<A0>>>
|
||||||
{
|
{
|
||||||
typedef shared_future<A0> type;
|
typedef shared_race_future<A0> type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename...Args1, typename...Args2>
|
template <typename...Args1, typename...Args2>
|
||||||
|
@ -124,6 +127,12 @@ struct future_copy_traits
|
||||||
eina::copy_from_c_traits<T>::copy_to_unitialized
|
eina::copy_from_c_traits<T>::copy_to_unitialized
|
||||||
(storage, info->value);
|
(storage, info->value);
|
||||||
}
|
}
|
||||||
|
static void copy_race(T* storage, Efl_Future_Event_Success const* info)
|
||||||
|
{
|
||||||
|
Efl_Future_Race_Success const* race = static_cast<Efl_Future_Race_Success const*>(info->value);
|
||||||
|
eina::copy_from_c_traits<T>::copy_to_unitialized
|
||||||
|
(storage, race->value);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename...Args>
|
template <typename...Args>
|
||||||
|
@ -150,29 +159,35 @@ struct future_copy_traits<eina::variant<Args...>>
|
||||||
else
|
else
|
||||||
copy_impl(storage, value, index, std::integral_constant<std::size_t, I+1>{}, max);
|
copy_impl(storage, value, index, std::integral_constant<std::size_t, I+1>{}, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void copy(eina::variant<Args...>* storage, Efl_Future_Event_Success const* other_info)
|
static void copy(eina::variant<Args...>* storage, Efl_Future_Event_Success const* other_info)
|
||||||
|
{
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
static void copy_race(eina::variant<Args...>* storage, Efl_Future_Event_Success const* other_info)
|
||||||
{
|
{
|
||||||
Efl_Future_Race_Success const* info = static_cast<Efl_Future_Race_Success const*>
|
Efl_Future_Race_Success const* info = static_cast<Efl_Future_Race_Success const*>
|
||||||
(static_cast<void const*>(other_info));
|
(static_cast<void const*>(other_info->value));
|
||||||
copy_impl(storage, info->value, info->index, std::integral_constant<std::size_t, 0ul>{}
|
copy_impl(storage, info->value, info->index, std::integral_constant<std::size_t, 0ul>{}
|
||||||
, std::integral_constant<std::size_t, sizeof...(Args)>{});
|
, std::integral_constant<std::size_t, sizeof...(Args)>{});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename A0, typename F>
|
template <typename A0, typename F, bool IsRace>
|
||||||
typename std::enable_if
|
typename std::enable_if
|
||||||
<
|
<
|
||||||
!std::is_same<A0, void>::value
|
!std::is_same<A0, void>::value
|
||||||
&& !std::is_same<typename std::result_of<F(A0)>::type, void>::value
|
&& !std::is_same<typename std::result_of<F(A0)>::type, void>::value
|
||||||
>::type
|
>::type
|
||||||
future_invoke(F f, Efl_Event const* event)
|
future_invoke(F f, Efl_Event const* event, std::integral_constant<bool, IsRace> /* is_race */)
|
||||||
{
|
{
|
||||||
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
typename std::aligned_storage<sizeof(A0), alignof(A0)>::type storage;
|
typename std::aligned_storage<sizeof(A0), alignof(A0)>::type storage;
|
||||||
future_copy_traits<A0>::copy(static_cast<A0*>(static_cast<void*>(&storage)), info);
|
if(IsRace)
|
||||||
|
future_copy_traits<A0>::copy_race(static_cast<A0*>(static_cast<void*>(&storage)), info);
|
||||||
|
else
|
||||||
|
future_copy_traits<A0>::copy(static_cast<A0*>(static_cast<void*>(&storage)), info);
|
||||||
auto r = f(*static_cast<A0*>(static_cast<void*>(&storage)));
|
auto r = f(*static_cast<A0*>(static_cast<void*>(&storage)));
|
||||||
typedef decltype(r) result_type;
|
typedef decltype(r) result_type;
|
||||||
typedef typename eina::alloc_to_c_traits<result_type>::c_type c_type;
|
typedef typename eina::alloc_to_c_traits<result_type>::c_type c_type;
|
||||||
|
@ -184,9 +199,9 @@ future_invoke(F f, Efl_Event const* event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename A0, typename F>
|
template <typename A0, typename F, bool IsRace>
|
||||||
typename std::enable_if<std::is_same<A0, void>::value>::type
|
typename std::enable_if<std::is_same<A0, void>::value>::type
|
||||||
future_invoke(F f, Efl_Event const* event)
|
future_invoke(F f, Efl_Event const* event, std::integral_constant<bool, IsRace>)
|
||||||
{
|
{
|
||||||
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
||||||
try
|
try
|
||||||
|
@ -230,8 +245,8 @@ static void future_invoke_impl_read_accessor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F, typename...Args, std::size_t...I>
|
template <typename F, typename...Args, std::size_t...I, bool IsRace>
|
||||||
void future_invoke_impl(F f, Efl_Event const* event, std::tuple<Args...>* arguments_dummy, eina::index_sequence<I...>)
|
void future_invoke_impl(F f, Efl_Event const* event, std::tuple<Args...>* arguments_dummy, std::integral_constant<bool, IsRace> race, eina::index_sequence<I...>)
|
||||||
{
|
{
|
||||||
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
|
||||||
try
|
try
|
||||||
|
@ -259,12 +274,12 @@ void future_invoke_impl(F f, Efl_Event const* event, std::tuple<Args...>* argume
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename A0, typename A1, typename...OtherArgs, typename F>
|
template <typename A0, typename A1, typename...OtherArgs, typename F, bool IsRace>
|
||||||
void
|
void
|
||||||
future_invoke(F f, Efl_Event const* event)
|
future_invoke(F f, Efl_Event const* event, std::integral_constant<bool, IsRace> race)
|
||||||
{
|
{
|
||||||
std::tuple<A0, A1, OtherArgs...>* p = nullptr;
|
std::tuple<A0, A1, OtherArgs...>* p = nullptr;
|
||||||
_impl::future_invoke_impl(f, event, p, eina::make_index_sequence<sizeof...(OtherArgs) + 2>{});
|
_impl::future_invoke_impl(f, event, p, race, eina::make_index_sequence<sizeof...(OtherArgs) + 2>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
|
@ -605,8 +605,8 @@ START_TEST(eo_cxx_future_race_construct_and_destroy)
|
||||||
|
|
||||||
efl::shared_future<int> future1(efl_ref(f1)), future2(efl_ref(f2));
|
efl::shared_future<int> future1(efl_ref(f1)), future2(efl_ref(f2));
|
||||||
efl::shared_future<double> future3(efl_ref(f2));
|
efl::shared_future<double> future3(efl_ref(f2));
|
||||||
efl::shared_future<int> race1 = race(future1, future2);
|
efl::shared_race_future<int> race1 = race(future1, future2);
|
||||||
efl::shared_future<efl::eina::variant<int, double>> race2 = race(future1, future3);
|
efl::shared_race_future<efl::eina::variant<int, double>> race2 = race(future1, future3);
|
||||||
}
|
}
|
||||||
ecore_shutdown();
|
ecore_shutdown();
|
||||||
}
|
}
|
||||||
|
@ -631,7 +631,7 @@ START_TEST(eo_cxx_future_race_wait)
|
||||||
|
|
||||||
efl::shared_future<int> future1(efl_ref(f1))
|
efl::shared_future<int> future1(efl_ref(f1))
|
||||||
, future2(efl_ref(f2));
|
, future2(efl_ref(f2));
|
||||||
efl::shared_future<int> future3 = race(future1, future2);
|
efl::shared_race_future<int> future3 = race(future1, future2);
|
||||||
|
|
||||||
std::thread thread([&]
|
std::thread thread([&]
|
||||||
{
|
{
|
||||||
|
@ -675,7 +675,7 @@ START_TEST(eo_cxx_future_race_get)
|
||||||
|
|
||||||
efl::shared_future<int> future1(efl_ref(f1))
|
efl::shared_future<int> future1(efl_ref(f1))
|
||||||
, future2(efl_ref(f2));
|
, future2(efl_ref(f2));
|
||||||
efl::shared_future<int> future3 = race(future1, future2);
|
efl::shared_race_future<int> future3 = race(future1, future2);
|
||||||
|
|
||||||
std::thread thread([&]
|
std::thread thread([&]
|
||||||
{
|
{
|
||||||
|
@ -720,7 +720,7 @@ START_TEST(eo_cxx_future_race_then_value)
|
||||||
fail_if(!f2);
|
fail_if(!f2);
|
||||||
|
|
||||||
efl::shared_future<int> future1(efl_ref(f1)), future2(efl_ref(f2));
|
efl::shared_future<int> future1(efl_ref(f1)), future2(efl_ref(f2));
|
||||||
efl::shared_future<int> future = race(future1, future2);
|
efl::shared_race_future<int> future = race(future1, future2);
|
||||||
efl::shared_future<int> rfuture;
|
efl::shared_future<int> rfuture;
|
||||||
|
|
||||||
std::thread thread
|
std::thread thread
|
||||||
|
@ -779,7 +779,7 @@ START_TEST(eo_cxx_future_race_variant_get)
|
||||||
|
|
||||||
efl::shared_future<int> future1(efl_ref(f1));
|
efl::shared_future<int> future1(efl_ref(f1));
|
||||||
efl::shared_future<double> future2(efl_ref(f2));
|
efl::shared_future<double> future2(efl_ref(f2));
|
||||||
efl::shared_future<efl::eina::variant<int, double>> future3 = race(future1, future2);
|
efl::shared_race_future<efl::eina::variant<int, double>> future3 = race(future1, future2);
|
||||||
|
|
||||||
std::thread thread([&]
|
std::thread thread([&]
|
||||||
{
|
{
|
||||||
|
@ -826,7 +826,7 @@ START_TEST(eo_cxx_future_race_variant_then_value)
|
||||||
|
|
||||||
efl::shared_future<int> future1(efl_ref(f1));
|
efl::shared_future<int> future1(efl_ref(f1));
|
||||||
efl::shared_future<double> future2(efl_ref(f2));
|
efl::shared_future<double> future2(efl_ref(f2));
|
||||||
efl::shared_future<efl::eina::variant<int, double>> future = race(future1, future2);
|
efl::shared_race_future<efl::eina::variant<int, double>> future = race(future1, future2);
|
||||||
efl::shared_future<int> rfuture;
|
efl::shared_future<int> rfuture;
|
||||||
|
|
||||||
std::thread thread
|
std::thread thread
|
||||||
|
|
Loading…
Reference in New Issue