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 <cedric.bail@free.fr>
This commit is contained in:
Felipe Magno de Almeida 2014-02-26 18:37:21 -03:00 committed by Cedric Bail
parent 3312ba4c56
commit 91f5a9b043
5 changed files with 456 additions and 0 deletions

View File

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

36
src/Makefile_Ecore_Cxx.am Normal file
View File

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

View File

@ -0,0 +1,123 @@
#ifndef _EFL_ECORE_CXX_ECORE_HH
#define _EFL_ECORE_CXX_ECORE_HH
#include <Ecore.h>
#include <utility>
#include <type_traits>
#include <memory>
#include <cstring>
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>
{
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()));
}
};
template <typename F>
void _ecore_main_loop_thread_safe_call_async_callback(void* data)
{
F* f = static_cast<F*>(data);
(*f)();
delete f;
}
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)());
}
template <typename F>
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<F>
, new F(std::move(f)) );
}
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);
}
struct ecore_init
{
ecore_init()
{
::ecore_init();
}
~ecore_init()
{
::ecore_shutdown();
}
};
} }
#endif

View File

@ -0,0 +1,103 @@
#include "Ecore.hh"
#include <cassert>
#include <algorithm>
#include <check.h>
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<char*>("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;
}

View File

@ -0,0 +1,193 @@
#include "Ecore.hh"
#include "Eina.hh"
#include <algorithm>
#include <iostream>
#include <check.h>
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<efl::eina::mutex> 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<efl::eina::mutex> 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);
}