forked from enlightenment/efl
ecore-cxx: add support for exceptions.
Summary: Added support for exceptions on ecore_main_loop_thread_safe_call_async and ecore_main_loop_thread_safe_call_sync. Also optimized the transport of the return value through a parameter on ecore_main_loop_thread_safe_call_sync. ecore-cxx: Changed uses of alignas for aligned_storage in C++11 Reviewers: cedric, raster CC: savio, cedric Differential Revision: https://phab.enlightenment.org/D593 Signed-off-by: Cedric BAIL <cedric.bail@samsung.com>
This commit is contained in:
parent
bc0a54c57c
commit
4105c002b3
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <Ecore.h>
|
#include <Ecore.h>
|
||||||
|
|
||||||
|
#include <Eina.hh>
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -10,83 +12,100 @@
|
||||||
|
|
||||||
namespace efl { namespace ecore {
|
namespace efl { namespace ecore {
|
||||||
|
|
||||||
template <typename T, typename Enable = void>
|
|
||||||
struct _ecore_result_type_marshaller;
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct _ecore_result_type_marshaller
|
struct _identity
|
||||||
<T, typename std::enable_if<std::is_pointer<T>::value>::type>
|
|
||||||
{
|
{
|
||||||
static void* to_void(T o)
|
typedef T type;
|
||||||
{
|
|
||||||
return static_cast<void*>(o);
|
|
||||||
}
|
|
||||||
static T from_void(void* o)
|
|
||||||
{
|
|
||||||
return static_cast<T>(o);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct _ecore_result_type_marshaller
|
|
||||||
<T, typename std::enable_if<!std::is_pointer<T>::value && std::is_pod<T>::value
|
|
||||||
&& sizeof(T) <= sizeof(void*)>::type>
|
|
||||||
{
|
|
||||||
static void* to_void(T&& o)
|
|
||||||
{
|
|
||||||
unsigned char buf[sizeof(void*)];
|
|
||||||
T* p = static_cast<T*>(static_cast<void*>(&buf[0]));
|
|
||||||
new (p) T(std::move(o));
|
|
||||||
void* store;
|
|
||||||
std::memcpy(&store, buf, sizeof(void*));
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
static T from_void(void* store)
|
|
||||||
{
|
|
||||||
T* p = static_cast<T*>(static_cast<void*>(&store));
|
|
||||||
struct destroy_T
|
|
||||||
{
|
|
||||||
destroy_T(T& p)
|
|
||||||
: p(p) {}
|
|
||||||
~destroy_T()
|
|
||||||
{
|
|
||||||
p.~T();
|
|
||||||
}
|
|
||||||
T& p;
|
|
||||||
} destroy(*p);
|
|
||||||
return T(std::move(*p));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct _ecore_result_type_marshaller
|
|
||||||
<T, typename std::enable_if<(sizeof(T) > sizeof(void*)) || !std::is_pod<T>::value>::type>
|
|
||||||
{
|
|
||||||
static void* to_void(T&& o)
|
|
||||||
{
|
|
||||||
return new T(o);
|
|
||||||
}
|
|
||||||
static T from_void(void* store)
|
|
||||||
{
|
|
||||||
std::unique_ptr<T> p(static_cast<T*>(store));
|
|
||||||
return T(std::move(*p.get()));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void _ecore_main_loop_thread_safe_call_async_callback(void* data)
|
void _ecore_main_loop_thread_safe_call_async_callback(void* data)
|
||||||
{
|
{
|
||||||
F* f = static_cast<F*>(data);
|
std::unique_ptr<F> f (static_cast<F*>(data));
|
||||||
|
try
|
||||||
|
{
|
||||||
(*f)();
|
(*f)();
|
||||||
delete f;
|
}
|
||||||
|
catch(std::bad_alloc const& e)
|
||||||
|
{
|
||||||
|
eina_error_set( ::EINA_ERROR_OUT_OF_MEMORY);
|
||||||
|
}
|
||||||
|
catch(std::system_error const& e)
|
||||||
|
{
|
||||||
|
efl::eina::set_error_code(e.code());
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
eina_error_set( efl::eina::unknown_error() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct _return_buffer
|
||||||
|
{
|
||||||
|
typename std::aligned_storage<sizeof(T),std::alignment_of<T>::value>::type buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct _return_buffer<void>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
struct _data
|
||||||
|
{
|
||||||
|
F& f;
|
||||||
|
std::exception_ptr exception;
|
||||||
|
typedef typename std::result_of<F()>::type result_type;
|
||||||
|
_return_buffer<result_type> return_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void* _ecore_main_loop_thread_safe_call_sync_callback_aux(_data<F>* d, _identity<void>)
|
||||||
|
{
|
||||||
|
d->f();
|
||||||
|
if(eina_error_get())
|
||||||
|
d->exception = make_exception_ptr(std::system_error(efl::eina::get_error_code()));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F, typename R>
|
||||||
|
void* _ecore_main_loop_thread_safe_call_sync_callback_aux(_data<F>* d, _identity<R>)
|
||||||
|
{
|
||||||
|
typedef R result_type;
|
||||||
|
new (&d->return_buffer.buffer) result_type ( std::move(d->f()) );
|
||||||
|
if(eina_error_get())
|
||||||
|
{
|
||||||
|
d->exception = make_exception_ptr(std::system_error(efl::eina::get_error_code()));
|
||||||
|
eina_error_set(0);
|
||||||
|
result_type* p = static_cast<result_type*>(static_cast<void*>(&d->return_buffer.buffer));
|
||||||
|
p->~result_type();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
void* _ecore_main_loop_thread_safe_call_sync_callback(void* data)
|
void* _ecore_main_loop_thread_safe_call_sync_callback(void* data)
|
||||||
{
|
{
|
||||||
F* f = static_cast<F*>(data);
|
_data<F>* d = static_cast<_data<F>*>(data);
|
||||||
typedef typename std::result_of<F()>::type result_type;
|
try
|
||||||
return _ecore_result_type_marshaller<result_type>::to_void((*f)());
|
{
|
||||||
|
return _ecore_main_loop_thread_safe_call_sync_callback_aux
|
||||||
|
(d, _identity<typename std::result_of<F()>::type>());
|
||||||
|
}
|
||||||
|
catch(std::bad_alloc const& e)
|
||||||
|
{
|
||||||
|
d->exception = std::current_exception();
|
||||||
|
}
|
||||||
|
catch(std::system_error const& e)
|
||||||
|
{
|
||||||
|
d->exception = std::current_exception();
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
d->exception = std::current_exception();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
|
@ -96,14 +115,48 @@ void main_loop_thread_safe_call_async(F&& f)
|
||||||
, new F(std::move(f)) );
|
, new F(std::move(f)) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void _get_return_value(_data<F>& data, _identity<void>)
|
||||||
|
{
|
||||||
|
if(data.exception)
|
||||||
|
{
|
||||||
|
std::rethrow_exception(data.exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F, typename R>
|
||||||
|
R _get_return_value(_data<F>& data, _identity<R>)
|
||||||
|
{
|
||||||
|
if(!data.exception)
|
||||||
|
{
|
||||||
|
R* b = static_cast<R*>(static_cast<void*>(&data.return_buffer.buffer));
|
||||||
|
struct destroy
|
||||||
|
{
|
||||||
|
destroy(R* p) : p(p)
|
||||||
|
{}
|
||||||
|
~destroy()
|
||||||
|
{
|
||||||
|
p->~R();
|
||||||
|
}
|
||||||
|
R* p;
|
||||||
|
} destroy_temp(b);
|
||||||
|
return std::move(*b);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::rethrow_exception(data.exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
typename std::result_of<F()>::type
|
typename std::result_of<F()>::type
|
||||||
main_loop_thread_safe_call_sync(F&& f)
|
main_loop_thread_safe_call_sync(F&& f)
|
||||||
{
|
{
|
||||||
void* d = ::ecore_main_loop_thread_safe_call_sync
|
|
||||||
(&ecore::_ecore_main_loop_thread_safe_call_sync_callback<F>, &f);
|
|
||||||
typedef typename std::result_of<F()>::type result_type;
|
typedef typename std::result_of<F()>::type result_type;
|
||||||
return _ecore_result_type_marshaller<result_type>::from_void(d);
|
_data<F> data {f};
|
||||||
|
::ecore_main_loop_thread_safe_call_sync
|
||||||
|
(&ecore::_ecore_main_loop_thread_safe_call_sync_callback<F>, &data);
|
||||||
|
return _get_return_value(data, _identity<result_type>());
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ecore_init
|
struct ecore_init
|
||||||
|
|
|
@ -13,6 +13,12 @@ using std::error_code;
|
||||||
using std::error_condition;
|
using std::error_condition;
|
||||||
typedef std::error_category system_error_category;
|
typedef std::error_category system_error_category;
|
||||||
|
|
||||||
|
inline Eina_Error unknown_error()
|
||||||
|
{
|
||||||
|
static Eina_Error error = eina_error_msg_static_register("Error from C++ from another value category error");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
inline system_error_category const& get_generic_category()
|
inline system_error_category const& get_generic_category()
|
||||||
{
|
{
|
||||||
return ::std::generic_category();
|
return ::std::generic_category();
|
||||||
|
@ -66,6 +72,14 @@ inline eina::error_code get_error_code()
|
||||||
return eina::error_code();
|
return eina::error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void set_error_code(eina::error_code const& e)
|
||||||
|
{
|
||||||
|
if(e.category() == eina_error_category())
|
||||||
|
eina_error_set(e.value());
|
||||||
|
else
|
||||||
|
eina_error_set(unknown_error());
|
||||||
|
}
|
||||||
|
|
||||||
inline eina::error_condition get_error_condition()
|
inline eina::error_condition get_error_condition()
|
||||||
{
|
{
|
||||||
Eina_Error error = eina_error_get();
|
Eina_Error error = eina_error_get();
|
||||||
|
|
|
@ -8,19 +8,28 @@
|
||||||
|
|
||||||
#include <check.h>
|
#include <check.h>
|
||||||
|
|
||||||
void call_async(efl::eina::mutex& mutex, efl::eina::condition_variable& cond, bool& done)
|
void call_async(efl::eina::mutex& mutex, efl::eina::condition_variable& cond, int& done)
|
||||||
{
|
{
|
||||||
efl::ecore::main_loop_thread_safe_call_async
|
efl::ecore::main_loop_thread_safe_call_async
|
||||||
(
|
(
|
||||||
[&mutex,&cond,&done]
|
[&mutex,&cond,&done]
|
||||||
{
|
{
|
||||||
std::cout << "yeah" << std::endl;
|
std::cout << "yeah" << std::endl;
|
||||||
ecore_main_loop_quit();
|
|
||||||
efl::eina::unique_lock<efl::eina::mutex> l(mutex);
|
efl::eina::unique_lock<efl::eina::mutex> l(mutex);
|
||||||
std::cout << "mutex locked" << std::endl;
|
++done;
|
||||||
done = true;
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
efl::ecore::main_loop_thread_safe_call_async
|
||||||
|
(
|
||||||
|
[&mutex,&cond,&done]
|
||||||
|
{
|
||||||
|
std::cout << "yeah2" << std::endl;
|
||||||
|
efl::eina::unique_lock<efl::eina::mutex> l(mutex);
|
||||||
|
++done;
|
||||||
cond.notify_one();
|
cond.notify_one();
|
||||||
std::cout << "exiting" << std::endl;
|
ecore_main_loop_quit();
|
||||||
|
throw std::bad_alloc();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +40,7 @@ START_TEST(ecore_cxx_safe_call_async)
|
||||||
|
|
||||||
efl::eina::mutex mutex;
|
efl::eina::mutex mutex;
|
||||||
efl::eina::condition_variable cond;
|
efl::eina::condition_variable cond;
|
||||||
bool done = false;
|
int done = 0;
|
||||||
efl::eina::thread thread(&call_async, std::ref(mutex), std::ref(cond), std::ref(done));
|
efl::eina::thread thread(&call_async, std::ref(mutex), std::ref(cond), std::ref(done));
|
||||||
|
|
||||||
ecore_main_loop_begin();
|
ecore_main_loop_begin();
|
||||||
|
@ -43,13 +52,15 @@ START_TEST(ecore_cxx_safe_call_async)
|
||||||
std::cout << "joined" << std::endl;
|
std::cout << "joined" << std::endl;
|
||||||
|
|
||||||
efl::eina::unique_lock<efl::eina::mutex> l(mutex);
|
efl::eina::unique_lock<efl::eina::mutex> l(mutex);
|
||||||
while(!done)
|
while(done != 2)
|
||||||
{
|
{
|
||||||
std::cout << "waiting" << std::endl;
|
std::cout << "wait" << std::endl;
|
||||||
cond.wait(l);
|
cond.wait(l);
|
||||||
std::cout << "waited" << std::endl;
|
std::cout << "waited" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ck_assert( ::eina_error_get() == ::EINA_ERROR_OUT_OF_MEMORY);
|
||||||
|
::eina_error_set(0);
|
||||||
std::cout << "end of ecore_cxx_safe_call_async" << std::endl;
|
std::cout << "end of ecore_cxx_safe_call_async" << std::endl;
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
@ -111,7 +122,14 @@ struct big_nonpod : big_pod
|
||||||
|
|
||||||
void call_sync_int()
|
void call_sync_int()
|
||||||
{
|
{
|
||||||
std::cout << "call_sync_init" << std::endl;
|
efl::ecore::main_loop_thread_safe_call_sync
|
||||||
|
(
|
||||||
|
[] () -> void
|
||||||
|
{
|
||||||
|
ck_assert( ::eina_error_get() == 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
int r1 =
|
int r1 =
|
||||||
efl::ecore::main_loop_thread_safe_call_sync
|
efl::ecore::main_loop_thread_safe_call_sync
|
||||||
(
|
(
|
||||||
|
@ -122,6 +140,37 @@ void call_sync_int()
|
||||||
);
|
);
|
||||||
ck_assert(r1 == 1);
|
ck_assert(r1 == 1);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
efl::ecore::main_loop_thread_safe_call_sync
|
||||||
|
(
|
||||||
|
[] () -> int
|
||||||
|
{
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ck_assert(false);
|
||||||
|
}
|
||||||
|
catch(std::bad_alloc const& e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
efl::ecore::main_loop_thread_safe_call_sync
|
||||||
|
(
|
||||||
|
[] () -> int
|
||||||
|
{
|
||||||
|
::eina_error_set( ::EINA_ERROR_OUT_OF_MEMORY);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ck_assert(false);
|
||||||
|
}
|
||||||
|
catch(std::system_error const& e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "big_pod" << std::endl;
|
std::cout << "big_pod" << std::endl;
|
||||||
|
|
||||||
big_pod r2 =
|
big_pod r2 =
|
||||||
|
@ -160,7 +209,6 @@ void call_sync_int()
|
||||||
[] () -> big_nonpod
|
[] () -> big_nonpod
|
||||||
{
|
{
|
||||||
std::cout << "before quit" << std::endl;
|
std::cout << "before quit" << std::endl;
|
||||||
ecore_main_loop_quit();
|
|
||||||
std::cout << "are we calling here" << std::endl;
|
std::cout << "are we calling here" << std::endl;
|
||||||
return big_nonpod();
|
return big_nonpod();
|
||||||
}
|
}
|
||||||
|
@ -169,6 +217,29 @@ void call_sync_int()
|
||||||
std::cout << "constructor_called: " << constructor_called << std::endl;
|
std::cout << "constructor_called: " << constructor_called << std::endl;
|
||||||
std::cout << "destructor_called: " << destructor_called << std::endl;
|
std::cout << "destructor_called: " << destructor_called << std::endl;
|
||||||
ck_assert(constructor_called == destructor_called);
|
ck_assert(constructor_called == destructor_called);
|
||||||
|
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
efl::ecore::main_loop_thread_safe_call_sync
|
||||||
|
(
|
||||||
|
[] () -> big_nonpod
|
||||||
|
{
|
||||||
|
std::cout << "before quit" << std::endl;
|
||||||
|
ecore_main_loop_quit();
|
||||||
|
std::cout << "are we calling here" << std::endl;
|
||||||
|
throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ck_assert(false);
|
||||||
|
}
|
||||||
|
catch(std::bad_alloc const& e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "constructor_called: " << constructor_called << std::endl;
|
||||||
|
std::cout << "destructor_called: " << destructor_called << std::endl;
|
||||||
|
ck_assert(constructor_called == destructor_called);
|
||||||
}
|
}
|
||||||
|
|
||||||
START_TEST(ecore_cxx_safe_call_sync)
|
START_TEST(ecore_cxx_safe_call_sync)
|
||||||
|
|
Loading…
Reference in New Issue