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:
Felipe Magno de Almeida 2014-03-07 14:33:07 +09:00 committed by Cedric BAIL
parent bc0a54c57c
commit 4105c002b3
3 changed files with 216 additions and 78 deletions

View File

@ -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

View File

@ -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();

View File

@ -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)