From 4105c002b3161d67600a22b2f570c4969a6b36b2 Mon Sep 17 00:00:00 2001 From: Felipe Magno de Almeida Date: Fri, 7 Mar 2014 14:33:07 +0900 Subject: [PATCH] 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 --- src/bindings/ecore_cxx/Ecore.hh | 189 +++++++++++------- src/bindings/eina_cxx/eina_error.hh | 14 ++ .../ecore_cxx/ecore_cxx_test_safe_call.cc | 91 ++++++++- 3 files changed, 216 insertions(+), 78 deletions(-) diff --git a/src/bindings/ecore_cxx/Ecore.hh b/src/bindings/ecore_cxx/Ecore.hh index 9d2dd0bf78..b121ba97cb 100644 --- a/src/bindings/ecore_cxx/Ecore.hh +++ b/src/bindings/ecore_cxx/Ecore.hh @@ -3,6 +3,8 @@ #include +#include + #include #include #include @@ -10,83 +12,100 @@ namespace efl { namespace ecore { -template -struct _ecore_result_type_marshaller; - template -struct _ecore_result_type_marshaller - ::value>::type> +struct _identity { - static void* to_void(T o) - { - return static_cast(o); - } - static T from_void(void* o) - { - return static_cast(o); - } -}; - -template -struct _ecore_result_type_marshaller - ::value && std::is_pod::value - && sizeof(T) <= sizeof(void*)>::type> -{ - static void* to_void(T&& o) - { - unsigned char buf[sizeof(void*)]; - T* p = static_cast(static_cast(&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(static_cast(&store)); - struct destroy_T - { - destroy_T(T& p) - : p(p) {} - ~destroy_T() - { - p.~T(); - } - T& p; - } destroy(*p); - return T(std::move(*p)); - } -}; - -template -struct _ecore_result_type_marshaller - sizeof(void*)) || !std::is_pod::value>::type> -{ - static void* to_void(T&& o) - { - return new T(o); - } - static T from_void(void* store) - { - std::unique_ptr p(static_cast(store)); - return T(std::move(*p.get())); - } + typedef T type; }; template void _ecore_main_loop_thread_safe_call_async_callback(void* data) { - F* f = static_cast(data); - (*f)(); - delete f; + std::unique_ptr f (static_cast(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 +struct _return_buffer +{ + typename std::aligned_storage::value>::type buffer; +}; + +template <> +struct _return_buffer +{ +}; + +template +struct _data +{ + F& f; + std::exception_ptr exception; + typedef typename std::result_of::type result_type; + _return_buffer return_buffer; +}; + +template +void* _ecore_main_loop_thread_safe_call_sync_callback_aux(_data* d, _identity) +{ + d->f(); + if(eina_error_get()) + d->exception = make_exception_ptr(std::system_error(efl::eina::get_error_code())); + return 0; +} + +template +void* _ecore_main_loop_thread_safe_call_sync_callback_aux(_data* d, _identity) +{ + 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(static_cast(&d->return_buffer.buffer)); + p->~result_type(); + } + return 0; } template void* _ecore_main_loop_thread_safe_call_sync_callback(void* data) { - F* f = static_cast(data); - typedef typename std::result_of::type result_type; - return _ecore_result_type_marshaller::to_void((*f)()); + _data* d = static_cast<_data*>(data); + try + { + return _ecore_main_loop_thread_safe_call_sync_callback_aux + (d, _identity::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 @@ -96,14 +115,48 @@ void main_loop_thread_safe_call_async(F&& f) , new F(std::move(f)) ); } +template +void _get_return_value(_data& data, _identity) +{ + if(data.exception) + { + std::rethrow_exception(data.exception); + } +} + +template +R _get_return_value(_data& data, _identity) +{ + if(!data.exception) + { + R* b = static_cast(static_cast(&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 std::result_of::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); typedef typename std::result_of::type result_type; - return _ecore_result_type_marshaller::from_void(d); + _data data {f}; + ::ecore_main_loop_thread_safe_call_sync + (&ecore::_ecore_main_loop_thread_safe_call_sync_callback, &data); + return _get_return_value(data, _identity()); } struct ecore_init diff --git a/src/bindings/eina_cxx/eina_error.hh b/src/bindings/eina_cxx/eina_error.hh index 1199e694b9..9962768799 100644 --- a/src/bindings/eina_cxx/eina_error.hh +++ b/src/bindings/eina_cxx/eina_error.hh @@ -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(); diff --git a/src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc b/src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc index bc2b78d4f3..9be7d73706 100644 --- a/src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc +++ b/src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc @@ -8,19 +8,28 @@ #include -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 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 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 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)