From 91f5a9b0431ed6ba1adab28fff0a3fab7fc65211 Mon Sep 17 00:00:00 2001 From: Felipe Magno de Almeida Date: Wed, 26 Feb 2014 18:37:21 -0300 Subject: [PATCH] ecore_cxx: add main_loop_thread_safe_call_sync and main_loop_thread_safe_call_async with tests The point of this binding is to enable the support for easy lambda for ecore function that wont be using Eo. See the tests on how to use those. Reviewers: cedric, raster CC: savio, cedric Differential Revision: https://phab.enlightenment.org/D582 Signed-off-by: Cedric Bail --- src/Makefile.am | 1 + src/Makefile_Ecore_Cxx.am | 36 ++++ src/bindings/ecore_cxx/Ecore.hh | 123 +++++++++++ src/tests/ecore_cxx/ecore_cxx_suite.cc | 103 ++++++++++ .../ecore_cxx/ecore_cxx_test_safe_call.cc | 193 ++++++++++++++++++ 5 files changed, 456 insertions(+) create mode 100644 src/Makefile_Ecore_Cxx.am create mode 100644 src/bindings/ecore_cxx/Ecore.hh create mode 100644 src/tests/ecore_cxx/ecore_cxx_suite.cc create mode 100644 src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc diff --git a/src/Makefile.am b/src/Makefile.am index 9abc8a919d..b2ac30d76a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,6 +27,7 @@ include Makefile_Eo.am include Makefile_Eet.am include Makefile_Evas.am include Makefile_Ecore.am +include Makefile_Ecore_Cxx.am include Makefile_Ecore_Con.am include Makefile_Ecore_Ipc.am include Makefile_Ecore_File.am diff --git a/src/Makefile_Ecore_Cxx.am b/src/Makefile_Ecore_Cxx.am new file mode 100644 index 0000000000..ff8efdca86 --- /dev/null +++ b/src/Makefile_Ecore_Cxx.am @@ -0,0 +1,36 @@ + +### Library + +installed_ecorecxxmainheadersdir = $(includedir)/ecore_cxx-@VMAJ@ +dist_installed_ecorecxxmainheaders_DATA = \ +bindings/ecore_cxx/Ecore.hh + +### Unit tests + +if EFL_ENABLE_TESTS +if HAVE_CXX11 + +check_PROGRAMS += tests/ecore_cxx/ecore_cxx_suite +TESTS += tests/ecore_cxx/ecore_cxx_suite + +tests_ecore_cxx_ecore_cxx_suite_SOURCES = \ +tests/ecore_cxx/ecore_cxx_suite.cc \ +tests/ecore_cxx/ecore_cxx_test_safe_call.cc + +tests_ecore_cxx_ecore_cxx_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ +-I$(top_srcdir)/src/bindings/ecore_cxx \ +-I$(top_srcdir)/src/bindings/eina_cxx \ +-DTESTS_SRC_DIR=\"$(top_srcdir)/src/tests/ecore_cxx\" \ +-DTESTS_BUILD_DIR=\"$(top_builddir)/src/tests/ecore_cxx\" \ +@CHECK_CFLAGS@ \ +@ECORE_CFLAGS@ \ +@EINA_CFLAGS@ + +tests_ecore_cxx_ecore_cxx_suite_LDADD = \ +@CHECK_LIBS@ \ +@USE_ECORE_LIBS@ +tests_ecore_cxx_ecore_cxx_suite_DEPENDENCIES = \ +@USE_ECORE_INTERNAL_LIBS@ + +endif +endif diff --git a/src/bindings/ecore_cxx/Ecore.hh b/src/bindings/ecore_cxx/Ecore.hh new file mode 100644 index 0000000000..9d2dd0bf78 --- /dev/null +++ b/src/bindings/ecore_cxx/Ecore.hh @@ -0,0 +1,123 @@ +#ifndef _EFL_ECORE_CXX_ECORE_HH +#define _EFL_ECORE_CXX_ECORE_HH + +#include + +#include +#include +#include +#include + +namespace efl { namespace ecore { + +template +struct _ecore_result_type_marshaller; + +template +struct _ecore_result_type_marshaller + ::value>::type> +{ + 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())); + } +}; + +template +void _ecore_main_loop_thread_safe_call_async_callback(void* data) +{ + F* f = static_cast(data); + (*f)(); + delete f; +} + +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)()); +} + +template +void main_loop_thread_safe_call_async(F&& f) +{ + ::ecore_main_loop_thread_safe_call_async( &ecore::_ecore_main_loop_thread_safe_call_async_callback + , new F(std::move(f)) ); +} + +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); +} + +struct ecore_init +{ + ecore_init() + { + ::ecore_init(); + } + ~ecore_init() + { + ::ecore_shutdown(); + } +}; + +} } + +#endif diff --git a/src/tests/ecore_cxx/ecore_cxx_suite.cc b/src/tests/ecore_cxx/ecore_cxx_suite.cc new file mode 100644 index 0000000000..9394672cd5 --- /dev/null +++ b/src/tests/ecore_cxx/ecore_cxx_suite.cc @@ -0,0 +1,103 @@ + +#include "Ecore.hh" + +#include +#include + +#include + +void ecore_test_safe_call(TCase* tc); + +typedef struct _Ecore_Test_Case Ecore_Test_Case; +struct _Ecore_Test_Case +{ + const char *test_case; + void (*build)(TCase *tc); +}; + +static const Ecore_Test_Case etc[] = { + { "Safe_Call", ecore_test_safe_call }, + { NULL, NULL } +}; + +static void +_list_tests(void) +{ + const Ecore_Test_Case *itr = etc; + fputs("Available Test Cases:\n", stderr); + for (; itr->test_case; itr++) + fprintf(stderr, "\t%s\n", itr->test_case); +} + +static Eina_Bool +_use_test(int argc, const char **argv, const char *test_case) +{ + if (argc < 1) + return 1; + + for (; argc > 0; argc--, argv++) + if (strcmp(test_case, *argv) == 0) + return 1; + + return 0; +} + +Suite * +ecore_build_suite(int argc, const char **argv) +{ + TCase *tc; + Suite *s; + int i; + + s = suite_create("Ecore"); + + for (i = 0; etc[i].test_case; ++i) + { + if (!_use_test(argc, argv, etc[i].test_case)) + continue; + + tc = tcase_create(etc[i].test_case); + tcase_set_timeout(tc, 0); + + etc[i].build(tc); + suite_add_tcase(s, tc); + } + + return s; +} + +int main(int argc, char* argv[]) +{ + Suite *s; + SRunner *sr; + int i, failed_count; + + for (i = 1; i < argc; i++) + if ((strcmp(argv[i], "-h") == 0) || + (strcmp(argv[i], "--help") == 0)) + { + fprintf(stderr, "Usage:\n\t%s [test_case1 .. [test_caseN]]\n", + argv[0]); + _list_tests(); + return 0; + } + else if ((strcmp(argv[i], "-l") == 0) || + (strcmp(argv[i], "--list") == 0)) + { + _list_tests(); + return 0; + } + + putenv(const_cast("EFL_RUN_IN_TREE=1")); + + s = ecore_build_suite(argc - 1, (const char **)argv + 1); + sr = srunner_create(s); + + srunner_set_xml(sr, TESTS_BUILD_DIR "/check-results.xml"); + + srunner_run_all(sr, CK_ENV); + failed_count = srunner_ntests_failed(sr); + srunner_free(sr); + + return (failed_count == 0) ? 0 : 255; +} diff --git a/src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc b/src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc new file mode 100644 index 0000000000..bc2b78d4f3 --- /dev/null +++ b/src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc @@ -0,0 +1,193 @@ + +#include "Ecore.hh" +#include "Eina.hh" + +#include + +#include + +#include + +void call_async(efl::eina::mutex& mutex, efl::eina::condition_variable& cond, bool& 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; + cond.notify_one(); + std::cout << "exiting" << std::endl; + } + ); +} + +START_TEST(ecore_cxx_safe_call_async) +{ + efl::ecore::ecore_init init; + + efl::eina::mutex mutex; + efl::eina::condition_variable cond; + bool done = false; + efl::eina::thread thread(&call_async, std::ref(mutex), std::ref(cond), std::ref(done)); + + ecore_main_loop_begin(); + + std::cout << "joining" << std::endl; + + thread.join(); + + std::cout << "joined" << std::endl; + + efl::eina::unique_lock l(mutex); + while(!done) + { + std::cout << "waiting" << std::endl; + cond.wait(l); + std::cout << "waited" << std::endl; + } + + std::cout << "end of ecore_cxx_safe_call_async" << std::endl; +} +END_TEST + +struct big_pod +{ + double x; + double y; +}; + +int constructor_called = 0 + , destructor_called = 0; + +struct small_nonpod +{ + small_nonpod() + : c(5) + { + constructor_called++; + } + small_nonpod(small_nonpod const& other) + : c(5) + { + ck_assert(other.c == 5); + constructor_called++; + } + ~small_nonpod() + { + ck_assert(c == 5); + destructor_called++; + } + char c; +}; + +struct big_nonpod : big_pod +{ + big_nonpod() + { + constructor_called++; + x = 2.0; + y = 1.0; + } + big_nonpod(big_nonpod const& other) + { + x = 2.0; + y = 1.0; + ck_assert(other.x == 2.0); + ck_assert(other.y == 1.0); + constructor_called++; + } + ~big_nonpod() + { + ck_assert(x == 2.0); + ck_assert(y == 1.0); + destructor_called++; + } + char c; +}; + +void call_sync_int() +{ + std::cout << "call_sync_init" << std::endl; + int r1 = + efl::ecore::main_loop_thread_safe_call_sync + ( + [] () -> int + { + return 1; + } + ); + ck_assert(r1 == 1); + + std::cout << "big_pod" << std::endl; + + big_pod r2 = + efl::ecore::main_loop_thread_safe_call_sync + ( + [] () -> big_pod + { + return {1.0, 2.0}; + } + ); + ck_assert(r2.x == 1.0); + ck_assert(r2.y == 2.0); + + std::cout << "small_nonpod" << std::endl; + + { + small_nonpod r3 = + efl::ecore::main_loop_thread_safe_call_sync + ( + [] () -> small_nonpod + { + return small_nonpod(); + } + ); + } + std::cout << "constructor_called: " << constructor_called << std::endl; + std::cout << "destructor_called: " << destructor_called << std::endl; + ck_assert(constructor_called == destructor_called); + + std::cout << "big_nonpod" << std::endl; + + { + big_nonpod r3 = + 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; + return big_nonpod(); + } + ); + } + 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) +{ + efl::ecore::ecore_init init; + + efl::eina::thread thread(&call_sync_int); + + ecore_main_loop_begin(); + + std::cout << "out of the loop" << std::endl; + + thread.join(); +} +END_TEST + +void +ecore_test_safe_call(TCase* tc) +{ + tcase_add_test(tc, ecore_cxx_safe_call_async); + tcase_add_test(tc, ecore_cxx_safe_call_sync); +}