forked from enlightenment/efl
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:
parent
3312ba4c56
commit
91f5a9b043
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue