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 <Eina.hh>
|
||||
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
|
@ -10,83 +12,100 @@
|
|||
|
||||
namespace efl { namespace ecore {
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct _ecore_result_type_marshaller;
|
||||
|
||||
template <typename T>
|
||||
struct _ecore_result_type_marshaller
|
||||
<T, typename std::enable_if<std::is_pointer<T>::value>::type>
|
||||
struct _identity
|
||||
{
|
||||
static void* to_void(T o)
|
||||
{
|
||||
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()));
|
||||
}
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
void _ecore_main_loop_thread_safe_call_async_callback(void* data)
|
||||
{
|
||||
F* f = static_cast<F*>(data);
|
||||
(*f)();
|
||||
delete f;
|
||||
std::unique_ptr<F> f (static_cast<F*>(data));
|
||||
try
|
||||
{
|
||||
(*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>
|
||||
void* _ecore_main_loop_thread_safe_call_sync_callback(void* data)
|
||||
{
|
||||
F* f = static_cast<F*>(data);
|
||||
typedef typename std::result_of<F()>::type result_type;
|
||||
return _ecore_result_type_marshaller<result_type>::to_void((*f)());
|
||||
_data<F>* d = static_cast<_data<F>*>(data);
|
||||
try
|
||||
{
|
||||
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>
|
||||
|
@ -96,14 +115,48 @@ void main_loop_thread_safe_call_async(F&& 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>
|
||||
typename std::result_of<F()>::type
|
||||
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;
|
||||
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
|
||||
|
|
|
@ -13,6 +13,12 @@ using std::error_code;
|
|||
using std::error_condition;
|
||||
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()
|
||||
{
|
||||
return ::std::generic_category();
|
||||
|
@ -66,6 +72,14 @@ inline eina::error_code get_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()
|
||||
{
|
||||
Eina_Error error = eina_error_get();
|
||||
|
|
|
@ -8,19 +8,28 @@
|
|||
|
||||
#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
|
||||
(
|
||||
[&mutex,&cond,&done]
|
||||
{
|
||||
std::cout << "yeah" << std::endl;
|
||||
ecore_main_loop_quit();
|
||||
efl::eina::unique_lock<efl::eina::mutex> l(mutex);
|
||||
std::cout << "mutex locked" << std::endl;
|
||||
done = true;
|
||||
++done;
|
||||
}
|
||||
);
|
||||
|
||||
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();
|
||||
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::condition_variable cond;
|
||||
bool done = false;
|
||||
int done = 0;
|
||||
efl::eina::thread thread(&call_async, std::ref(mutex), std::ref(cond), std::ref(done));
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
@ -43,13 +52,15 @@ START_TEST(ecore_cxx_safe_call_async)
|
|||
std::cout << "joined" << std::endl;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
END_TEST
|
||||
|
@ -111,7 +122,14 @@ struct big_nonpod : big_pod
|
|||
|
||||
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 =
|
||||
efl::ecore::main_loop_thread_safe_call_sync
|
||||
(
|
||||
|
@ -122,6 +140,37 @@ void call_sync_int()
|
|||
);
|
||||
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;
|
||||
|
||||
big_pod r2 =
|
||||
|
@ -160,7 +209,6 @@ void call_sync_int()
|
|||
[] () -> big_nonpod
|
||||
{
|
||||
std::cout << "before quit" << std::endl;
|
||||
ecore_main_loop_quit();
|
||||
std::cout << "are we calling here" << std::endl;
|
||||
return big_nonpod();
|
||||
}
|
||||
|
@ -169,6 +217,29 @@ void call_sync_int()
|
|||
std::cout << "constructor_called: " << constructor_called << std::endl;
|
||||
std::cout << "destructor_called: " << destructor_called << std::endl;
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue