efl/src/tests/eo/suite/eo_test_general.c

1881 lines
47 KiB
C
Raw Normal View History

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#ifndef _WIN32
# include <signal.h>
# include <unistd.h>
#endif
#include <Eo.h>
#include "eo_internal.h"
#include "eo_suite.h"
2012-09-25 23:56:52 -07:00
#include "eo_test_class_simple.h"
#include "eo_test_class_singleton.h"
#include "eo_test_domain.h"
/* Loading this internal header for testing purposes. */
#include "eo_ptr_indirection.h"
EFL_START_TEST(eo_simple)
{
Eo *obj = efl_add_ref(EO_CLASS, NULL);
fail_if(obj);
obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj);
efl_constructor(obj);
efl_destructor(obj);
efl_unref(obj);
}
EFL_END_TEST
EFL_START_TEST(eo_singleton)
{
Eo *obj = efl_add_ref(SINGLETON_CLASS, NULL);
fail_if(!obj);
Eo *obj2 = efl_add_ref(SINGLETON_CLASS, NULL);
fail_if(!obj2);
ck_assert_ptr_eq(obj, obj2);
efl_unref(obj);
efl_unref(obj2);
}
EFL_END_TEST
#define OVERRIDE_A_SIMPLE 100859
#define OVERRIDE_A 324000
static int
_simple_obj_override_a_get(Eo *obj, void *class_data EINA_UNUSED)
{
return OVERRIDE_A + simple_a_get(efl_super(obj, EFL_OBJECT_OVERRIDE_CLASS));
}
static void
_simple_obj_override_a_double_set(Eo *obj, void *class_data EINA_UNUSED, int a)
{
simple_a_set(efl_super(obj, EFL_OBJECT_OVERRIDE_CLASS), 2 * a);
}
eo: Rename EAPI macro to EO_API in Eo library Summary: Patch from a series of patches to rename EAPI symbols to specific library DSOs. = The Rationale = This patch is from a series of patches to rename EAPI symbols to specific library DSOs. EAPI was designed to be able to pass `__attribute__ ((visibility ("default")))` for symbols with GCC, which would mean that even if -fvisibility=hidden was used when compiling the library, the needed symbols would get exported. MSVC __almost__ works like GCC (or mingw) in which you can declare everything as export and it will just work (slower, but it will work). But there's a caveat: global variables will not work the same way for MSVC, but works for mingw and GCC. For global variables (as opposed to functions), MSVC requires correct DSO visibility for MSVC: instead of declaring a symbol as export for everything, you need to declare it as import when importing from another DSO and export when defining it locally. With current EAPI definitions, we get the following example working in mingw and MSVC (observe it doesn't define any global variables as exported symbols). Example 1: dll1: ``` EAPI void foo(void); EAPI void bar() { foo(); } ``` dll2: ``` EAPI void foo() { printf ("foo\n"); } ``` This works fine with API defined as __declspec(dllexport) in both cases and for gcc defining as `__atttribute__((visibility("default")))`. However, the following: Example 2: dll1: ``` EAPI extern int foo; EAPI void foobar(void); EAPI void bar() { foo = 5; foobar(); } ``` dll2: ``` EAPI int foo = 0; EAPI void foobar() { printf ("foo %d\n", foo); } ``` This will work on mingw but will not work for MSVC. And that's why LIBAPI is the only solution that works for MSVC. Co-authored-by: João Paulo Taylor Ienczak Zanette <jpaulotiz@gmail.com> Co-authored-by: Lucas Cavalcante de Sousa <lucks.sousa@gmail.com> Co-authored-by: Ricardo Campos <ricardo.campos@expertise.dev> Reviewers: jptiz, lucas, vtorri, woohyun Reviewed By: jptiz, lucas, vtorri Subscribers: cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D12203
2020-12-09 08:50:29 -08:00
int test_class_beef_get(const Efl_Object *obj);
EFL_FUNC_BODY_CONST(test_class_beef_get, int, 0)
static int
_simple_obj_override_beef_get(Eo *obj, void *class_data EINA_UNUSED)
{
test_class_beef_get(efl_super(obj, EFL_OBJECT_OVERRIDE_CLASS));
return 1337;
}
EFL_START_TEST(efl_object_override_tests)
{
Eo *obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj);
/* First get the value before the override to make sure it works and to
* make sure we don't cache. */
ck_assert_int_eq(simple_a_get(obj), 0);
/* Test override */
EFL_OPS_DEFINE(
overrides,
EFL_OBJECT_OP_FUNC(simple_a_get, _simple_obj_override_a_get));
fail_if(!efl_object_override(obj, &overrides));
ck_assert_int_eq(simple_a_get(obj), OVERRIDE_A);
/* Check non-overriden functions work. */
simple_a_set(obj, OVERRIDE_A_SIMPLE);
ck_assert_int_eq(simple_a_get(obj), OVERRIDE_A + OVERRIDE_A_SIMPLE);
/* Override again. */
EFL_OPS_DEFINE(
overrides2,
EFL_OBJECT_OP_FUNC(simple_a_set, _simple_obj_override_a_double_set));
fail_if(!efl_object_override(obj, NULL));
fail_if(!efl_object_override(obj, &overrides2));
simple_a_set(obj, OVERRIDE_A_SIMPLE);
ck_assert_int_eq(simple_a_get(obj), OVERRIDE_A_SIMPLE * 2);
/* Try overriding again, allowed since 1.21. */
fail_if(!efl_object_override(obj, &overrides));
ck_assert_int_eq(simple_a_get(obj), OVERRIDE_A + OVERRIDE_A_SIMPLE * 2);
/* Try introducing a new function */
EFL_OPS_DEFINE(
overrides3,
EFL_OBJECT_OP_FUNC(test_class_beef_get, _simple_obj_override_beef_get));
fail_if(efl_object_override(obj, &overrides3));
fail_if(!efl_object_override(obj, NULL));
fail_if(efl_object_override(obj, &overrides3));
/* Test override reset of all functions */
fail_if(!efl_object_override(obj, NULL));
simple_a_set(obj, 42 * OVERRIDE_A_SIMPLE);
ck_assert_int_eq(simple_a_get(obj), 42 * OVERRIDE_A_SIMPLE);
/* Test override reset of a single functions, allowed since 1.21. */
EFL_OPS_DEFINE(
overrides4,
EFL_OBJECT_OP_FUNC(simple_a_get, NULL));
fail_if(!efl_object_override(obj, NULL));
simple_a_set(obj, 1337);
ck_assert_int_eq(simple_a_get(obj), 1337);
fail_if(!efl_object_override(obj, &overrides));
fail_if(!efl_object_override(obj, &overrides2));
ck_assert_int_eq(simple_a_get(obj), OVERRIDE_A + 1337);
fail_if(!efl_object_override(obj, &overrides4));
ck_assert_int_eq(simple_a_get(obj), 1337);
efl_unref(obj);
}
EFL_END_TEST
static int _eo_signals_cb_current = 0;
static int _eo_signals_cb_flag = 0;
static void
_eo_signals_a_changed_cb(void *_data, const Efl_Event *event EINA_UNUSED)
{
int data = (intptr_t) _data;
_eo_signals_cb_current++;
ck_assert_int_eq(data, _eo_signals_cb_current);
_eo_signals_cb_flag |= 0x1;
}
static void
_eo_signals_a_changed_cb2(void *_data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
{
_eo_signals_cb_flag |= 0x2;
efl_event_callback_stop(event->object);
2013-04-12 05:46:30 -07:00
}
static void
_eo_signals_a_changed_within_cb(void *_data EINA_UNUSED, const Efl_Event *event)
{
int a = 3;
efl_event_callback_legacy_call(event->object, EV_A_CHANGED, &a);
_eo_signals_cb_flag = 0x8;
}
static void
_eo_signals_a_changed_never(void *_data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
2013-04-12 05:46:30 -07:00
{
/* This one should never be called. */
fail_if(1);
}
static void
_eo_signals_efl_del_cb(void *_data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
{
_eo_signals_cb_flag |= 0x4;
}
2018-03-07 11:01:25 -08:00
static int check_is_deled = 0;
void
2018-03-07 11:01:25 -08:00
_eo_signals_cb_added_deled(void *data EINA_UNUSED, const Efl_Event *event)
{
const Efl_Callback_Array_Item_Full *callback_array = event->info;
if (check_is_deled)
fail_if(callback_array->func == _eo_signals_cb_added_deled);
else
fail_if(callback_array->func != _eo_signals_cb_added_deled);
}
EFL_CALLBACKS_ARRAY_DEFINE(_eo_signals_callbacks,
{ EV_A_CHANGED, _eo_signals_a_changed_cb },
{ EV_A_CHANGED, _eo_signals_a_changed_cb2 },
{ EV_A_CHANGED, _eo_signals_a_changed_never },
{ EFL_EVENT_DEL, _eo_signals_efl_del_cb });
EFL_START_TEST(eo_signals)
{
Eo *obj = efl_add_ref(SIMPLE_CLASS, NULL);
Eina_Bool r;
2014-04-02 01:46:34 -07:00
efl_event_callback_add(obj, EFL_EVENT_CALLBACK_ADD, _eo_signals_cb_added_deled, &_eo_signals_callbacks);
r = efl_event_callback_add(obj, EFL_EVENT_CALLBACK_DEL, _eo_signals_cb_added_deled, &_eo_signals_callbacks);
fail_if(!r);
check_is_deled = 1;
efl_event_callback_array_priority_add(obj, _eo_signals_callbacks(), -100, (void *) 1);
efl_event_callback_array_add(obj, _eo_signals_callbacks(), (void *) 3);
r = efl_event_callback_array_priority_add(obj, _eo_signals_callbacks(), -50, (void *) 2);
fail_if(!r);
simple_a_set(obj, 1);
ck_assert_int_eq(_eo_signals_cb_flag, 0x3);
efl_event_callback_array_del(obj, _eo_signals_callbacks(), (void *) 1);
efl_event_callback_array_del(obj, _eo_signals_callbacks(), (void *) 2);
r = efl_event_callback_array_del(obj, _eo_signals_callbacks(), (void *) 3);
fail_if(!r);
2013-04-12 05:46:30 -07:00
/* Try to delete something that doesn't exist. */
r = efl_event_callback_array_del(obj, _eo_signals_callbacks(), (void *) 4);
fail_if(r);
_eo_signals_cb_flag = 0;
simple_a_set(obj, 1);
ck_assert_int_eq(_eo_signals_cb_flag, 0x0);
r = efl_event_callback_array_add(obj, NULL, NULL);
fail_if(r);
efl_unref(obj);
obj = efl_add_ref(SIMPLE_CLASS, NULL);
/* Legacy support signals. */
{
const Efl_Event_Description *a_desc = efl_object_legacy_only_event_description_get("a,changed");
fail_if(!a_desc);
ck_assert_str_eq(a_desc->name, "a,changed");
fail_if(a_desc == EV_A_CHANGED);
/* Check that when calling again we still get the same event. */
const Efl_Event_Description *a_desc2 = efl_object_legacy_only_event_description_get("a,changed");
fail_if(!a_desc2);
fail_if(a_desc2 != a_desc);
const Efl_Event_Description *bad_desc = efl_object_legacy_only_event_description_get("bad");
fail_if(!bad_desc);
ck_assert_str_eq(bad_desc->name, "bad");
/* Call Eo event with legacy and non-legacy callbacks. */
_eo_signals_cb_current = 0;
efl_event_callback_priority_add(obj, EV_A_CHANGED2, -1000, _eo_signals_a_changed_never, (void *) 1);
efl_event_callback_priority_add(obj, EV_A_CHANGED2, 0, _eo_signals_a_changed_within_cb, NULL);
efl_event_callback_priority_add(obj, EV_A_CHANGED, -100, _eo_signals_a_changed_cb, (void *) 1);
efl_event_callback_add(obj, a_desc, _eo_signals_a_changed_cb2, NULL);
simple_a_set(obj, 1);
ck_assert_int_eq(_eo_signals_cb_flag, 0x3);
/* We don't need this one anymore. */
r = efl_event_callback_del(obj, EV_A_CHANGED2, _eo_signals_a_changed_never, (void *) 1);
fail_if(!r);
r = efl_event_callback_del(obj, a_desc, _eo_signals_a_changed_cb2, NULL);
fail_if(!r);
efl_event_callback_add(obj, EV_A_CHANGED, _eo_signals_a_changed_cb2, NULL);
/* Call legacy event with legacy and non-legacy callbacks. */
int a = 3;
_eo_signals_cb_current = 0;
_eo_signals_cb_flag = 0;
efl_event_callback_legacy_call(obj, a_desc, &a);
ck_assert_int_eq(_eo_signals_cb_flag, 0x3);
/* Stop event within event. */
_eo_signals_cb_current = 0;
_eo_signals_cb_flag = 0;
fail_if(!efl_event_callback_legacy_call(obj, EV_A_CHANGED2, &a));
ck_assert_int_eq(_eo_signals_cb_flag, 0x8);
fail_if(!r);
}
efl_unref(obj);
}
EFL_END_TEST
EFL_START_TEST(efl_data_fetch)
{
/* Usually should be const, not const only for the test... */
static Efl_Class_Description class_desc = {
2014-04-02 01:46:34 -07:00
EO_VERSION,
"Simple2",
EFL_CLASS_TYPE_REGULAR,
10,
NULL,
NULL,
NULL
};
const Efl_Class *klass = efl_class_new(&class_desc, EO_CLASS, NULL);
fail_if(!klass);
Eo *obj = efl_add_ref(klass, NULL);
fail_if(!obj);
#ifdef EO_DEBUG
fail_if(efl_data_scope_get(obj, SIMPLE_CLASS));
#endif
efl_unref(obj);
class_desc.data_size = 0;
klass = efl_class_new(&class_desc, EO_CLASS, NULL);
fail_if(!klass);
obj = efl_add_ref(klass, NULL);
fail_if(!obj);
fail_if(efl_data_scope_get(obj, klass));
fail_if(!efl_data_scope_get(obj, EFL_OBJECT_CLASS));
efl_unref(obj);
}
EFL_END_TEST
EFL_START_TEST(efl_data_safe_fetch)
{
Eo *obj = efl_add_ref(SIMPLE2_CLASS, NULL);
fail_if(!obj || !efl_data_scope_safe_get(obj, SIMPLE2_CLASS));
efl_unref(obj);
obj = efl_add_ref(SIMPLE3_CLASS, NULL);
fail_if(!obj);
fail_if(!efl_isa(obj, SIMPLE_CLASS));
fail_if(!efl_isa(obj, SIMPLE2_CLASS));
fail_if(!efl_isa(obj, SIMPLE3_CLASS));
fail_if(!efl_isa(SIMPLE3_CLASS, SIMPLE_CLASS));
fail_if(!efl_isa(SIMPLE_CLASS, SIMPLE_CLASS));
fail_if(!efl_data_scope_safe_get(obj, SIMPLE_CLASS));
fail_if(!efl_data_scope_safe_get(obj, SIMPLE3_CLASS));
fail_if(efl_data_scope_safe_get(obj, SIMPLE2_CLASS) != NULL);
fail_if(efl_data_scope_safe_get(NULL, EFL_OBJECT_CLASS) != NULL);
efl_unref(obj);
fail_if(efl_data_scope_safe_get(obj, SIMPLE3_CLASS) != NULL);
}
EFL_END_TEST
EFL_START_TEST(efl_isa_tests)
{
const Efl_Class *klass, *iface, *mixin;
{
/* Usually should be const, not const only for the test... */
static Efl_Class_Description class_desc = {
2014-04-02 01:46:34 -07:00
EO_VERSION,
"Iface",
EFL_CLASS_TYPE_INTERFACE,
0,
NULL,
NULL,
NULL
};
iface = efl_class_new(&class_desc, NULL, NULL);
fail_if(!iface);
}
{
/* Usually should be const, not const only for the test... */
static Efl_Class_Description class_desc = {
2014-04-02 01:46:34 -07:00
EO_VERSION,
"Mixin",
EFL_CLASS_TYPE_MIXIN,
0,
NULL,
NULL,
NULL
};
mixin = efl_class_new(&class_desc, NULL, NULL);
fail_if(!mixin);
}
{
/* Usually should be const, not const only for the test... */
static Efl_Class_Description class_desc = {
2014-04-02 01:46:34 -07:00
EO_VERSION,
"Simple2",
EFL_CLASS_TYPE_REGULAR,
10,
NULL,
NULL,
NULL
};
klass = efl_class_new(&class_desc, EO_CLASS, iface, mixin, NULL);
fail_if(!klass);
}
Eo *obj = efl_add_ref(klass, NULL);
fail_if(!obj);
fail_if(efl_isa(obj, SIMPLE_CLASS));
fail_if(!efl_isa(obj, iface));
fail_if(!efl_isa(obj, mixin));
fail_if(!efl_isa(obj, klass));
fail_if(!efl_isa(obj, EO_CLASS));
efl_unref(obj);
obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj);
fail_if(efl_isa(obj, klass));
fail_if(efl_isa(obj, iface));
fail_if(efl_isa(obj, mixin));
fail_if(!efl_isa(obj, SIMPLE_CLASS));
fail_if(!efl_isa(obj, EO_CLASS));
efl_unref(obj);
}
EFL_END_TEST
EFL_START_TEST(efl_composite_tests)
{
Eo *obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj);
Eo *obj2 = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj2);
Eo *obj3 = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj3);
efl_composite_attach(obj, obj2);
fail_if(!efl_composite_part_is(obj2));
/* Check swapping attachments works. */
efl_composite_attach(obj3, obj2);
fail_if(!efl_composite_part_is(obj2));
/* Check that a deletion of a child detaches from the parent. */
efl_unref(obj2);
fail_if(!efl_composite_attach(obj3, obj));
/* Check that a deletion of the parent detaches the child. */
efl_unref(obj3);
fail_if(efl_composite_part_is(obj));
efl_unref(obj);
}
EFL_END_TEST
static Eina_Bool _man_should_con = EINA_TRUE;
static Eina_Bool _man_should_des = EINA_TRUE;
static const Efl_Class *cur_klass = NULL;
static Eo *
_man_con(Eo *obj, void *data EINA_UNUSED, va_list *list EINA_UNUSED)
{
if (_man_should_con)
efl_manual_free_set(obj, EINA_TRUE);
return efl_constructor(efl_super(obj, cur_klass));
}
static void
_man_des(Eo *obj, void *data EINA_UNUSED, va_list *list EINA_UNUSED)
{
efl_destructor(efl_super(obj, cur_klass));
if (_man_should_des)
efl_manual_free_set(obj, EINA_FALSE);
}
static Eina_Bool
_class_initializer(Efl_Class *klass)
{
EFL_OPS_DEFINE(ops,
EFL_OBJECT_OP_FUNC(efl_constructor, _man_con),
EFL_OBJECT_OP_FUNC(efl_destructor, _man_des),
);
return efl_class_functions_set(klass, &ops, NULL);
}
EFL_START_TEST(eo_man_free)
{
/* Usually should be const, not const only for the test... */
static Efl_Class_Description class_desc = {
2014-04-02 01:46:34 -07:00
EO_VERSION,
"Simple2",
EFL_CLASS_TYPE_REGULAR,
10,
_class_initializer,
NULL,
NULL
};
const Efl_Class *klass = efl_class_new(&class_desc, EO_CLASS, NULL);
fail_if(!klass);
cur_klass = klass;
Eo *obj = efl_add_ref(klass, NULL);
fail_if(!obj);
efl_unref(obj);
obj = efl_add_ref(klass, NULL);
fail_if(!obj);
fail_if(efl_manual_free(obj));
efl_unref(obj);
_man_should_des = EINA_FALSE;
klass = efl_class_new(&class_desc, EO_CLASS, NULL);
cur_klass = klass;
fail_if(!klass);
obj = efl_add_ref(klass, NULL);
fail_if(!obj);
fail_if(efl_manual_free(obj));
fail_if(efl_destructed_is(obj));
efl_unref(obj);
fail_if(!efl_destructed_is(obj));
fail_if(!efl_manual_free(obj));
obj = efl_add_ref(klass, NULL);
fail_if(!obj);
efl_unref(obj);
fail_if(!efl_destructed_is(obj));
fail_if(!efl_manual_free(obj));
_man_should_con = EINA_FALSE;
klass = efl_class_new(&class_desc, EO_CLASS, NULL);
cur_klass = klass;
fail_if(!klass);
obj = efl_add_ref(klass, NULL);
fail_if(!obj);
fail_if(efl_manual_free(obj));
efl_unref(obj);
obj = efl_add_ref(klass, NULL);
fail_if(!obj);
efl_manual_free_set(obj, EINA_TRUE);
efl_unref(obj);
efl_ref(obj);
efl_unref(obj);
DISABLE_ABORT_ON_CRITICAL_START;
efl_unref(obj);
DISABLE_ABORT_ON_CRITICAL_END;
fail_if(!efl_manual_free(obj));
obj = efl_add_ref(klass, NULL);
fail_if(!obj);
efl_manual_free_set(obj, EINA_TRUE);
efl_unref(obj);
efl_ref(obj);
efl_unref(obj);
DISABLE_ABORT_ON_CRITICAL_START;
efl_unref(obj);
efl_unref(obj);
efl_unref(obj);
DISABLE_ABORT_ON_CRITICAL_END;
fail_if(!efl_manual_free(obj));
}
EFL_END_TEST
EFL_START_TEST(efl_refs)
{
Eo *obj = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj2 = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj3 = efl_add_ref(SIMPLE_CLASS, NULL);
efl_xref(obj, obj2);
fail_if(efl_ref_count(obj) != 2);
efl_xref(obj, obj3);
fail_if(efl_ref_count(obj) != 3);
efl_xunref(obj, obj2);
fail_if(efl_ref_count(obj) != 2);
efl_xunref(obj, obj3);
fail_if(efl_ref_count(obj) != 1);
#ifdef EO_DEBUG
efl_xunref(obj, obj3);
fail_if(efl_ref_count(obj) != 1);
efl_xref(obj, obj2);
fail_if(efl_ref_count(obj) != 2);
efl_xunref(obj, obj3);
fail_if(efl_ref_count(obj) != 2);
efl_xunref(obj, obj2);
fail_if(efl_ref_count(obj) != 1);
#endif
/* Check we don't seg if there's an extra xref. */
efl_xref(obj, obj2);
efl_unref(obj);
efl_unref(obj);
efl_unref(obj2);
efl_unref(obj3);
/* Check hierarchy */
obj = efl_add_ref(SIMPLE_CLASS, NULL);
obj2 = efl_ref(efl_add(SIMPLE_CLASS, obj));
Eo *wref = NULL;
efl_wref_add(obj2, &wref);
fail_if(!wref);
efl_unref(obj2);
fail_if(!wref); /* Parent is still holding a reference. */
efl_unref(obj);
fail_if(wref);
/* efl_add_ref and normal efl_add */
obj = efl_add_ref(SIMPLE_CLASS, NULL);
obj2 = efl_add(SIMPLE_CLASS, obj);
obj3 = efl_add_ref(SIMPLE_CLASS, obj);
ck_assert_int_eq(efl_ref_count(obj), 1);
ck_assert_int_eq(efl_ref_count(obj2), 1);
ck_assert_int_eq(efl_ref_count(obj3), 2);
efl_unref(obj);
2016-10-21 07:18:46 -07:00
efl_del(obj2);
efl_unref(obj3);
efl_del(obj3);
/* Setting and removing parents. */
obj = efl_add_ref(SIMPLE_CLASS, NULL);
obj2 = efl_ref(efl_add(SIMPLE_CLASS, obj));
obj3 = efl_add_ref(SIMPLE_CLASS, NULL);
efl_parent_set(obj, obj);
ck_assert_ptr_eq(efl_parent_get(obj), NULL);
efl_parent_set(obj2, obj3);
efl_parent_set(obj3, obj);
ck_assert_int_eq(efl_ref_count(obj2), 2);
ck_assert_int_eq(efl_ref_count(obj3), 2);
efl_parent_set(obj2, NULL);
efl_parent_set(obj3, NULL);
ck_assert_int_eq(efl_ref_count(obj2), 1);
ck_assert_int_eq(efl_ref_count(obj3), 1);
fail_if(!efl_invalidated_get(obj2));
fail_if(!efl_invalidated_get(obj3));
efl_unref(obj);
efl_unref(obj2);
efl_unref(obj3);
/* Setting and removing parents for add_ref */
obj = efl_add_ref(SIMPLE_CLASS, NULL);
obj2 = efl_add_ref(SIMPLE_CLASS, obj);
obj3 = efl_add_ref(SIMPLE_CLASS, NULL);
ck_assert_int_eq(efl_ref_count(obj2), 2);
ck_assert_int_eq(efl_ref_count(obj3), 1);
efl_parent_set(obj2, obj3);
efl_parent_set(obj3, obj);
ck_assert_int_eq(efl_ref_count(obj2), 2);
ck_assert_int_eq(efl_ref_count(obj3), 2);
efl_parent_set(obj2, NULL);
efl_parent_set(obj3, NULL);
ck_assert_int_eq(efl_ref_count(obj2), 1);
ck_assert_int_eq(efl_ref_count(obj3), 1);
fail_if(!efl_invalidated_get(obj2));
fail_if(!efl_invalidated_get(obj3));
{
int ref_pre = efl_ref_count(obj2);
efl_parent_set(obj2, obj3);
ck_assert_int_eq(ref_pre, efl_ref_count(obj2));
}
efl_unref(obj);
efl_unref(obj2);
efl_unref(obj3);
/* Just check it doesn't seg atm. */
obj = efl_add_ref(SIMPLE_CLASS, NULL);
efl_ref(obj);
efl_unref(obj);
efl_unref(obj);
obj = efl_add_ref(SIMPLE_CLASS, NULL);
obj2 = efl_add(SIMPLE_CLASS, obj);
DISABLE_ABORT_ON_CRITICAL_START;
efl_unref(obj2);
DISABLE_ABORT_ON_CRITICAL_END;
efl_ref(obj2);
efl_del(obj2);
efl_unref(obj);
}
EFL_END_TEST
EFL_START_TEST(efl_weak_reference)
{
Eo *obj = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj2 = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *wref = NULL, *wref2 = NULL, *wref3 = NULL;
efl_wref_add(obj, &wref);
fail_if(!wref);
efl_unref(obj);
fail_if(wref);
obj = efl_add_ref(SIMPLE_CLASS, NULL);
efl_wref_add(obj, &wref);
efl_ref(obj);
fail_if(!wref);
efl_unref(obj);
fail_if(!wref);
efl_unref(obj);
fail_if(wref);
obj = efl_add_ref(SIMPLE_CLASS, NULL);
efl_wref_add(obj, &wref);
efl_wref_del(obj, &wref);
fail_if(wref);
efl_wref_add(obj, &wref);
efl_wref_del(obj2, &wref);
fail_if(!wref);
efl_wref_del_safe(&wref);
fail_if(wref);
wref = obj;
efl_wref_del(obj, &wref);
fail_if(wref);
wref = wref2 = wref3 = NULL;
efl_wref_add(obj, &wref);
efl_wref_add(obj, &wref2);
efl_wref_add(obj, &wref3);
fail_if(!wref);
fail_if(!wref2);
fail_if(!wref3);
efl_wref_del(obj, &wref);
efl_wref_del(obj, &wref2);
efl_wref_del(obj, &wref3);
fail_if(wref);
fail_if(wref2);
fail_if(wref3);
efl_wref_add(obj, &wref2);
efl_wref_add(obj, &wref3);
wref = obj;
efl_wref_del(obj, &wref);
fail_if(wref);
efl_wref_del(obj, &wref2);
efl_wref_del(obj, &wref3);
efl_unref(obj);
efl_unref(obj2);
}
EFL_END_TEST
EFL_START_TEST(eo_generic_data)
{
Eo *obj = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj2 = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj3 = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *objtmp;
void *data = NULL;
Eina_Value *value;
Eina_Value *value2;
efl_key_data_set(obj, "test1", (void *) 1);
data = efl_key_data_get(obj, "test1");
fail_if(1 != (intptr_t) data);
efl_key_data_set(obj, "test1", NULL);
data = efl_key_data_get(obj, "test1");
fail_if(data);
efl_key_data_set(obj, "test1", (void *) 1);
efl_key_data_set(obj, "test2", (void *) 2);
data = efl_key_data_get(obj, "test1");
fail_if(1 != (intptr_t) data);
data = efl_key_data_get(obj, "test2");
fail_if(2 != (intptr_t) data);
data = efl_key_data_get(obj, "test2");
fail_if(2 != (intptr_t) data);
efl_key_data_set(obj, "test2", NULL);
data = efl_key_data_get(obj, "test2");
fail_if(data);
data = efl_key_data_get(obj, "test1");
fail_if(1 != (intptr_t) data);
efl_key_data_set(obj, "test1", NULL);
data = efl_key_data_get(obj, "test1");
fail_if(data);
efl_key_ref_set(obj, "test1", obj2);
objtmp = efl_key_ref_get(obj, "test1");
fail_if(obj2 != objtmp);
efl_key_ref_set(obj, "test1", NULL);
objtmp = efl_key_ref_get(obj, "test1");
fail_if(objtmp);
efl_key_ref_set(obj, "test1", obj2);
fail_if(efl_ref_count(obj2) != 2);
efl_key_ref_set(obj, "test2", obj3);
fail_if(efl_ref_count(obj3) != 2);
objtmp = efl_key_ref_get(obj, "test1");
fail_if(obj2 != objtmp);
objtmp = efl_key_ref_get(obj, "test2");
fail_if(obj3 != objtmp);
data = efl_key_ref_get(obj, "test2");
fail_if(obj3 != objtmp);
efl_key_ref_set(obj, "test2", NULL);
fail_if(efl_ref_count(obj3) != 1);
objtmp = efl_key_ref_get(obj, "test2");
fail_if(objtmp);
objtmp = efl_key_ref_get(obj, "test1");
fail_if(obj2 != objtmp);
efl_key_ref_set(obj, "test1", NULL);
fail_if(efl_ref_count(obj2) != 1);
objtmp = efl_key_ref_get(obj, "test1");
fail_if(objtmp);
efl_key_ref_set(obj, "test1", obj2);
efl_key_ref_set(obj, "test2", obj3);
efl_unref(obj2);
efl_unref(obj2);
efl_unref(obj3);
efl_unref(obj3);
objtmp = efl_key_ref_get(obj, "test1");
fail_if(objtmp);
objtmp = efl_key_ref_get(obj, "test2");
fail_if(objtmp);
obj2 = efl_add_ref(SIMPLE_CLASS, NULL);
obj3 = efl_add_ref(SIMPLE_CLASS, NULL);
efl_key_wref_set(obj, "test1", obj2);
objtmp = efl_key_wref_get(obj, "test1");
fail_if(obj2 != objtmp);
efl_key_wref_set(obj, "test1", NULL);
objtmp = efl_key_wref_get(obj, "test1");
fail_if(objtmp);
efl_key_wref_set(obj, "test1", obj2);
fail_if(efl_ref_count(obj2) != 1);
efl_key_wref_set(obj, "test2", obj3);
fail_if(efl_ref_count(obj3) != 1);
objtmp = efl_key_wref_get(obj, "test1");
fail_if(obj2 != objtmp);
objtmp = efl_key_wref_get(obj, "test2");
fail_if(obj3 != objtmp);
data = efl_key_wref_get(obj, "test2");
fail_if(obj3 != objtmp);
efl_key_wref_set(obj, "test2", NULL);
fail_if(efl_ref_count(obj3) != 1);
objtmp = efl_key_wref_get(obj, "test2");
fail_if(objtmp);
objtmp = efl_key_wref_get(obj, "test1");
fail_if(obj2 != objtmp);
efl_key_wref_set(obj, "test1", NULL);
fail_if(efl_ref_count(obj2) != 1);
objtmp = efl_key_wref_get(obj, "test1");
fail_if(objtmp);
efl_key_wref_set(obj, "test1", obj2);
efl_key_wref_set(obj, "test2", obj3);
efl_unref(obj2);
efl_unref(obj3);
objtmp = efl_key_wref_get(obj, "test1");
fail_if(objtmp);
objtmp = efl_key_wref_get(obj, "test2");
fail_if(objtmp);
value = eina_value_new(EINA_VALUE_TYPE_INT);
eina_value_set(value, 1234);
value2 = efl_key_value_get(obj, "value1");
fail_if(value2 != NULL);
efl_key_value_set(obj, "value1", value);
value2 = efl_key_value_get(obj, "value1");
fail_if(value != value2);
efl_key_value_set(obj, "value1", NULL);
value2 = efl_key_value_get(obj, "value1");
fail_if(value2 != NULL);
efl_key_value_set(obj, "value1", NULL);
efl_unref(obj);
efl_unref(obj2);
efl_unref(obj3);
}
EFL_END_TEST
EFL_START_TEST(eo_magic_checks)
{
char _buf[sizeof(long)]; /* Just enough to hold eina magic + a bit more. */
char *buf = _buf;
memset(_buf, 1, sizeof(_buf));
Eo *obj = efl_add(SIMPLE_CLASS, (Eo *) buf);
fail_if(obj);
while (1)
{
int i = 20, a = 0;
2013-09-24 23:29:19 -07:00
Eo *parent = NULL;
Eo *wref = NULL;
Eo *obj2 = NULL;
2013-09-24 23:29:19 -07:00
obj = efl_add((Efl_Class *) buf, NULL);
fail_if(obj);
obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj);
simple_a_set((Eo *) buf, ++i);
a = simple_a_get((Eo *) buf);
ck_assert_int_ne(i, a);
simple_a_set(efl_super((Eo *) buf, SIMPLE_CLASS), ++i);
a = simple_a_get(efl_super((Eo *) buf, SIMPLE_CLASS));
ck_assert_int_ne(i, a);
simple_a_set(efl_super(obj, (const Efl_Class *) buf), ++i);
a = simple_a_get(efl_super(obj, (const Efl_Class *) buf));
ck_assert_int_ne(i, a);
fail_if(efl_class_get((Eo *) buf));
fail_if(efl_class_name_get((Efl_Class*) buf));
fail_if(efl_class_get(obj) != SIMPLE_CLASS);
fail_if(efl_class_get(SIMPLE_CLASS) != EFL_CLASS_CLASS);
simple_a_set((Efl_Class *) buf, 1);
simple_a_set(efl_super((Efl_Class *) buf, SIMPLE_CLASS), ++i);
simple_a_set(efl_super(SIMPLE_CLASS, (Efl_Class *) buf), ++i);
fail_if(efl_class_new(NULL, (Efl_Class *) buf), NULL);
efl_xref(obj, (Eo *) buf);
efl_xunref(obj, (Eo *) buf);
efl_xref((Eo *) buf, obj);
efl_xunref((Eo *) buf, obj);
efl_ref((Eo *) buf);
efl_unref((Eo *) buf);
efl_del((Eo *) buf);
efl_isa((Eo *) buf, SIMPLE_CLASS);
efl_isa(obj, (Efl_Class *) buf);
fail_if(0 != efl_ref_count((Eo *) buf));
efl_wref_add((Eo *) buf, &wref);
parent = efl_parent_get((Eo *) buf);
fail_if(wref);
2013-09-24 23:29:19 -07:00
fail_if(parent);
fail_if(efl_data_scope_get((Eo *) buf, SIMPLE_CLASS));
efl_composite_attach(obj, (Eo *) buf);
efl_composite_detach(obj, (Eo *) buf);
efl_composite_part_is((Eo *) buf);
efl_event_callback_forwarder_add(obj, NULL, (Eo *) buf);
efl_event_callback_forwarder_del(obj, NULL, (Eo *) buf);
efl_manual_free_set((Eo *) buf, EINA_TRUE);
efl_manual_free((Eo *) buf);
efl_destructed_is((Eo *) buf);
obj2 = NULL;
efl_parent_set(obj, (Eo *) buf);
obj2 = efl_parent_get(obj);
fail_if(obj2 && (obj2 == (Eo *) buf));
efl_unref(obj);
if (!buf)
break;
else
buf = NULL;
}
}
EFL_END_TEST
/* resolve issues */
static Eina_Bool
_a_print(Eo *obj EINA_UNUSED, void *class_data EINA_UNUSED)
{
printf("Hey\n");
return EINA_TRUE;
}
EFL_FUNC_BODY(resolve_a_print, Eina_Bool, EINA_FALSE);
static Eina_Bool
_multi_class_initializer(Efl_Class *klass)
{
EFL_OPS_DEFINE(ops,
EFL_OBJECT_OP_FUNC(resolve_a_print, _a_print),
);
return efl_class_functions_set(klass, &ops, NULL);
}
EFL_START_TEST(efl_func_resolve)
{
/* Usually should be const, not const only for the test... */
static Efl_Class_Description class_desc = {
EO_VERSION,
"Inherit",
EFL_CLASS_TYPE_REGULAR,
0,
_multi_class_initializer,
NULL,
NULL
};
const Efl_Class *klass = efl_class_new(&class_desc, SIMPLE_CLASS, NULL);
fail_if(!klass);
Eo *obj = efl_add_ref(klass, NULL);
fail_if(!resolve_a_print(obj));
efl_unref(obj);
obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj);
efl_manual_free_set(obj, EINA_TRUE);
fail_if(resolve_a_print(obj));
efl_unref(obj);
fail_if(!efl_destructed_is(obj));
efl_manual_free(obj);
}
EFL_END_TEST
EFL_START_TEST(efl_add_do_and_custom)
{
Simple_Public_Data *pd = NULL;
Eo *obj = NULL;
obj = efl_add_ref(SIMPLE_CLASS, NULL, efl_constructor(efl_added));
fail_if(!obj);
efl_unref(obj);
obj = efl_add_ref(SIMPLE_CLASS, NULL, simple_a_set(efl_added, 7));
fail_if(!obj);
pd = efl_data_scope_get(obj, SIMPLE_CLASS);
fail_if(pd->a != 7);
efl_unref(obj);
obj = efl_add_ref(SIMPLE_CLASS, NULL, efl_constructor(efl_added), simple_a_set(efl_added, 7));
fail_if(!obj);
pd = efl_data_scope_get(obj, SIMPLE_CLASS);
fail_if(pd->a != 7);
efl_unref(obj);
Eina_Bool finalized;
obj = efl_add_ref(SIMPLE_CLASS, NULL, finalized = efl_finalized_get(efl_added));
fail_if(finalized);
finalized = efl_finalized_get(obj);
fail_if(!finalized);
efl_unref(obj);
}
EFL_END_TEST
EFL_START_TEST(eo_pointers_indirection)
{
static const Efl_Class_Description class_desc = {
2014-04-02 01:46:34 -07:00
EO_VERSION,
"Simple",
EFL_CLASS_TYPE_REGULAR,
0,
NULL,
NULL,
NULL
};
const Efl_Class *klass = efl_class_new(&class_desc, EO_CLASS, NULL);
fail_if(!klass);
/* Check simple id validity */
Eo *obj = efl_add_ref(klass, NULL);
fail_if(!obj);
fail_if(!efl_isa(obj, klass));
obj = (Eo *)((char *)(obj) + 1);
fail_if(efl_isa(obj, klass));
obj = (Eo *)((char *)(obj) - 1);
fail_if(!efl_isa(obj, klass));
efl_unref(obj);
fail_if(efl_isa(obj, klass));
/* Check id invalidity after deletion */
Eo *obj1 = efl_add_ref(klass, NULL);
fail_if(!obj1);
efl_unref(obj1);
Eo *obj2 = efl_add_ref(klass, NULL);
fail_if(!obj2);
fail_if(!efl_isa(obj2, klass));
fail_if(efl_isa(obj1, klass));
efl_unref(obj2);
/* Check id sanity checks for "close enough" ids. */
obj1 = efl_add_ref(klass, NULL);
fail_if(!obj1);
obj2 = (Eo *) (((Eo_Id) obj1) & ~MASK_OBJ_TAG);
fail_if(efl_class_get(obj2));
efl_unref(obj1);
#define NB_OBJS 10000
unsigned int obj_id;
Eo **objs = calloc(NB_OBJS, sizeof(Eo *));
fail_if(!objs);
/* Creation of the objects */
for ( obj_id = 0; obj_id < NB_OBJS; obj_id++)
{
objs[obj_id] = efl_add_ref(klass, NULL);
if(!objs[obj_id])
fail_if(!objs[obj_id]);
if(!efl_isa(objs[obj_id], klass))
fail_if(!efl_isa(objs[obj_id], klass));
}
/* Deletion of a few objects */
for ( obj_id = 0; obj_id < NB_OBJS; obj_id+=2000)
{
efl_unref(objs[obj_id]);
if(efl_isa(objs[obj_id], klass))
fail_if(efl_isa(objs[obj_id], klass));
}
/* Creation of the deleted objects */
for ( obj_id = 0; obj_id < NB_OBJS; obj_id+=2000)
{
objs[obj_id] = efl_add_ref(klass, NULL);
if(!objs[obj_id])
fail_if(!objs[obj_id]);
if(!efl_isa(objs[obj_id], klass))
fail_if(!efl_isa(objs[obj_id], klass));
}
/* Deletion of all the objects */
for ( obj_id = 0; obj_id < NB_OBJS; obj_id++)
efl_unref(objs[obj_id]);
/* Just be sure that we trigger an already freed error */
efl_unref(objs[0]);
free(objs);
}
EFL_END_TEST
static Eo *
_efl_add_failures_finalize(Eo *obj EINA_UNUSED, void *class_data EINA_UNUSED)
{
return NULL;
}
static Eina_Bool
_add_failures_class_initializer(Efl_Class *klass)
{
EFL_OPS_DEFINE(ops,
EFL_OBJECT_OP_FUNC(efl_finalize, _efl_add_failures_finalize),
);
return efl_class_functions_set(klass, &ops, NULL);
}
EFL_START_TEST(efl_add_failures)
{
static const Efl_Class_Description class_desc = {
EO_VERSION,
"Simple2",
EFL_CLASS_TYPE_REGULAR,
0,
_add_failures_class_initializer,
NULL,
NULL
};
const Efl_Class *klass = efl_class_new(&class_desc, EO_CLASS, NULL);
Eo *obj = efl_add_ref(klass, NULL);
fail_if(obj);
}
EFL_END_TEST
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
static Eina_Bool intercepted = EINA_FALSE;
static void
_del_intercept(Eo *obj)
{
intercepted = EINA_TRUE;
efl_del_intercept_set(obj, NULL);
efl_unref(obj);
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
}
static void
_del_intercept_reuse(Eo *obj)
{
efl_reuse(obj);
}
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
EFL_START_TEST(efl_del_intercept)
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
{
static const Efl_Class_Description class_desc = {
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
EO_VERSION,
"Simple",
EFL_CLASS_TYPE_REGULAR,
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
0,
NULL,
NULL,
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
NULL
};
const Efl_Class *klass = efl_class_new(&class_desc, EO_CLASS, NULL);
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
fail_if(!klass);
/* Check unref interception */
intercepted = EINA_FALSE;
Eo *obj = efl_add_ref(klass, NULL);
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
fail_if(!obj);
fail_if(!efl_isa(obj, klass));
efl_del_intercept_set(obj, _del_intercept);
efl_unref(obj);
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
fail_if(!intercepted);
fail_if(efl_isa(obj, klass));
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
/* Check del interception */
intercepted = EINA_FALSE;
obj = efl_add_ref(klass, NULL);
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
fail_if(!obj);
fail_if(!efl_isa(obj, klass));
efl_del_intercept_set(obj, _del_intercept);
efl_unref(obj);
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
fail_if(!intercepted);
fail_if(efl_isa(obj, klass));
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
/* Check reuse works as expected. */
Eo *parent = efl_add_ref(SIMPLE_CLASS, NULL);
obj = efl_add_ref(klass, NULL);
fail_if(!obj);
ck_assert_int_eq(efl_ref_count(obj), 1);
efl_parent_set(obj, parent);
ck_assert_int_eq(efl_ref_count(obj), 2);
efl_del_intercept_set(obj, _del_intercept_reuse);
efl_del_intercept_set(obj, NULL);
/* This essentially checks it get unsunk */
ck_assert_int_eq(efl_ref_count(obj), 2);
efl_parent_set(obj, parent);
ck_assert_int_eq(efl_ref_count(obj), 2);
efl_unref(obj);
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
}
EFL_END_TEST
eo del interceptor: add the ability to intercept deletions of eo objects Imagine this. You have an object. You pass this object handle as a message to another thread. Let's say it's not a UI object, so something you might expect to be able to be accessed from multiple threads. In order to keep the object alive you eo_ref() it when placing the message on a queue and eo_unref() it once the message is "done" in the other thread. If the original sender unref()ed the object before the message is done, then the object will be destroyed in the reciever thread. This is bad for objects "expecting" not to be destroyed outside their owning thread. This allows thius situation to be fixed. A constructor in a class of an object can set up a delete interceptor. For example if we have a "loop ownership" class you multi-ple-inherit from/use as a mixin. This class will set up the interceptor to ensure that on destruction if pthread_self() != owning loop thread id, then add object to "delete me" queue on the owning loop and wake it up. the owning loop thread will wake up and then process this queue and delete the queued objects nicely and safely within the "owning context". This can also be used in this same manner to defer deletion within a loop "until later" in the same delete_me queue. You can even use this as a caching mechanism for objects to prevernt their actual destruction and instead place them in a cached area to be picked from at a later date. The uses are many for this and this is a basic building block for future EFL features like generic messages where a message payload could be an eo object and thus the above loop onwership issue can happen and needs fixing. This adds APIs, implementation, documentation (doxy reference) and tests. @feature
2016-03-07 23:57:22 -08:00
EFL_START_TEST(efl_name)
{
Eo *obj = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj2 = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *obj3 = efl_add_ref(SIMPLE_CLASS, NULL);
Eo *objtmp;
const char *id;
id = efl_name_get(obj);
fail_if(NULL != id);
efl_name_set(obj, "Hello");
id = efl_name_get(obj);
fail_if(NULL == id);
fail_if(!!strcmp(id, "Hello"));
efl_name_set(obj, "Hello");
efl_name_set(obj, "");
id = efl_name_get(obj);
fail_if(NULL != id);
efl_name_set(obj, "Hello");
efl_name_set(obj, NULL);
id = efl_name_get(obj);
fail_if(NULL != id);
efl_name_set(obj2, "joe");
efl_name_set(obj3, "bob");
efl_parent_set(obj2, obj);
efl_parent_set(obj3, obj2);
objtmp = efl_name_find(obj, "bob");
fail_if(objtmp != obj3);
objtmp = efl_name_find(obj, "joe");
fail_if(objtmp != obj2);
objtmp = efl_name_find(obj, "bo*");
fail_if(objtmp != obj3);
objtmp = efl_name_find(obj, "*oe");
fail_if(objtmp != obj2);
objtmp = efl_name_find(obj, "Simple:*oe");
fail_if(objtmp != obj2);
objtmp = efl_name_find(obj, "*mple:joe");
fail_if(objtmp != obj2);
efl_unref(obj);
}
EFL_END_TEST
EFL_START_TEST(eo_comment)
{
Eo *obj = efl_add_ref(SIMPLE_CLASS, NULL);
const char *comment;
comment = efl_comment_get(obj);
fail_if(NULL != comment);
efl_comment_set(obj, "Hello");
comment = efl_comment_get(obj);
fail_if(NULL == comment);
fail_if(!!strcmp(comment, "Hello"));
efl_comment_set(obj, "Hello");
efl_comment_set(obj, "");
comment = efl_comment_get(obj);
fail_if(NULL != comment);
efl_comment_set(obj, "Hello");
efl_comment_set(obj, NULL);
comment = efl_comment_get(obj);
fail_if(NULL != comment);
efl_unref(obj);
}
EFL_END_TEST
EFL_START_TEST(eo_rec_interface)
{
Eo *s = efl_add_ref(SEARCHABLE_CLASS, NULL);
Eo *obj = efl_add(SIMPLE_CLASS, s);
Eo *obj2 = efl_add(SIMPLE_CLASS, obj);
Eo *objtmp;
objtmp = efl_provider_find(obj2, SEARCHABLE_CLASS);
fail_if(objtmp != s);
efl_del(obj);
}
EFL_END_TEST
typedef struct
{
Eo *obj, *objs;
Efl_Domain_Data *dat;
} Data;
static void *
thr1(void *data, Eina_Thread t EINA_UNUSED)
{
Data *d = data;
Efl_Id_Domain dom;
Eo *s1, *s2;
fail_if(efl_domain_switch(EFL_ID_DOMAIN_THREAD) != EINA_TRUE);
fail_if(efl_domain_get() != EFL_ID_DOMAIN_THREAD);
printf("ADD2\n");
Eo *obj = efl_add_ref(DOMAIN_CLASS, NULL);
printf("ADD2 DONE = %p\n", obj);
printf("VERIFY finalized_get()\n");
fail_if(!efl_finalized_get(d->objs));
s2 = efl_add_ref(DOMAIN_CLASS, obj);
ck_assert(s2);
efl_parent_set(s2, NULL);
efl_unref(s2);
printf("VERIFY parent_set(invalid) -- WILL SHOW ERRORS\n");
efl_domain_current_push(EFL_ID_DOMAIN_SHARED);
s1 = efl_add_ref(DOMAIN_CLASS, NULL);
efl_domain_current_pop();
efl_unref(s1);
efl_parent_set(d->objs, s1);
printf("END OF ERRORS\n");
printf("VERIFY parent_set(invalid2) -- WILL SHOW ERRORS\n");
efl_domain_current_push(EFL_ID_DOMAIN_SHARED);
s1 = efl_add_ref(DOMAIN_CLASS, NULL);
s2 = efl_add_ref(DOMAIN_CLASS, s1);
efl_domain_current_pop();
efl_parent_set(s2, NULL);
efl_parent_set(s1, s2);
efl_unref(s1);
efl_unref(s2);
printf("END OF ERRORS\n");
printf("VERIFY composite\n");
efl_domain_current_push(EFL_ID_DOMAIN_SHARED);
s1 = efl_add_ref(SIMPLE_CLASS, NULL, simple_a_set(efl_added, 7));
s2 = efl_add_ref(SIMPLE_CLASS, NULL, simple_a_set(efl_added, 42));
efl_domain_current_pop();
efl_composite_attach(d->objs, s1);
int i1 = simple_a_get(d->objs);
int i2 = simple_a_get(s1);
fail_if(i1 != i2);
fail_if(efl_composite_attach(d->objs, s2));
efl_unref(s1);
fail_if(!efl_composite_attach(d->objs, s2));
efl_unref(s2);
printf("SET ON LOCAL\n");
domain_a_set(obj, 1234);
fail_if(domain_a_get(obj) != 1234);
printf("SET ON LOCAL DONE\n");
printf("SET ON SHARED\n");
domain_a_set(d->objs, 1234);
fail_if(domain_a_get(d->objs) != 1234);
printf("SET ON SHARED DONE\n");
printf("FAAAAIL check %p\n", d->obj);
domain_a_set(d->obj, 1234);
int v = domain_a_get(d->obj);
printf("........ v = %i\n", v);
fail_if(v == 1234);
printf("FAAAAIL DONE\n");
printf("ADOPT...\n");
dom = efl_domain_data_adopt(d->dat);
fail_if(dom != EFL_ID_DOMAIN_MAIN);
printf("SUCCEED check %p\n", d->obj);
domain_a_set(d->obj, 8910);
v = domain_a_get(d->obj);
printf("........ v = %i\n", v);
fail_if(v != 8910);
printf("SUCCEED DONE\n");
printf("RETURN DOMAIN DATA\n");
fail_if(efl_domain_data_return(dom) != EINA_TRUE);
printf("RETURN DOMAIN DATA DONE\n");
efl_unref(obj);
return NULL;
}
#ifndef _WIN32
static void
_timeout(int val EINA_UNUSED)
{
EINA_LOG_CRIT("TIMED OUT!");
exit(-1);
}
#endif
EFL_START_TEST(eo_domain)
{
Eo *obj, *objs;
printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
#ifndef _WIN32
if (!eina_streq(getenv("CK_FORK"), "no"))
{
signal(SIGALRM, _timeout);
alarm(10);
}
#endif
fail_if(efl_domain_get() != EFL_ID_DOMAIN_MAIN);
fail_if(efl_domain_switch(EFL_ID_DOMAIN_THREAD) != EINA_TRUE);
fail_if(efl_domain_get() != EFL_ID_DOMAIN_THREAD);
fail_if(efl_domain_switch(EFL_ID_DOMAIN_MAIN) != EINA_TRUE);
fail_if(efl_domain_get() != EFL_ID_DOMAIN_MAIN);
fail_if(efl_domain_current_get() != EFL_ID_DOMAIN_MAIN);
fail_if(efl_domain_current_set(EFL_ID_DOMAIN_SHARED) != EINA_TRUE);
fail_if(efl_domain_current_get() != EFL_ID_DOMAIN_SHARED);
fail_if(efl_domain_current_set(EFL_ID_DOMAIN_MAIN) != EINA_TRUE);
fail_if(efl_domain_current_get() != EFL_ID_DOMAIN_MAIN);
fail_if(efl_domain_current_push(EFL_ID_DOMAIN_SHARED) != EINA_TRUE);
fail_if(efl_domain_current_get() != EFL_ID_DOMAIN_SHARED);
fail_if(efl_domain_current_push(EFL_ID_DOMAIN_THREAD) != EINA_TRUE);
fail_if(efl_domain_current_get() != EFL_ID_DOMAIN_THREAD);
efl_domain_current_pop();
fail_if(efl_domain_current_get() != EFL_ID_DOMAIN_SHARED);
efl_domain_current_pop();
fail_if(efl_domain_current_get() != EFL_ID_DOMAIN_MAIN);
printf("ADD1\n");
obj = efl_add_ref(DOMAIN_CLASS, NULL);
printf("ADD1 DONE = %p\n", obj);
efl_domain_current_push(EFL_ID_DOMAIN_SHARED);
printf("ADDS\n");
objs = efl_add_ref(DOMAIN_CLASS, NULL, domain_a_set(efl_added, 42));
printf("ADDS DONE = %p\n", objs);
efl_domain_current_pop();
fail_if(efl_compatible(objs, obj) == EINA_TRUE);
domain_a_set(obj, 1234);
fail_if(domain_a_get(obj) != 1234);
domain_a_set(objs, 1234);
fail_if(domain_a_get(objs) != 1234);
ck_assert(SIMPLE_CLASS);
Eina_Thread t;
Data data;
data.obj = obj;
data.objs = objs;
printf("GET DOMAIN DATA\n");
data.dat = efl_domain_data_get();
fail_if(data.dat == NULL);
printf("THR CREATE\n");
fail_if(!eina_thread_create(&t, EINA_THREAD_NORMAL, -1, thr1, &data));
printf("JOIN\n");
eina_thread_join(t);
printf("JOIN DONE\n");
printf("DELETING OBJECTS\n");
efl_unref(obj);
efl_unref(objs);
printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
#ifndef _WIN32
alarm(0);
signal(SIGALRM, NULL);
#endif
}
EFL_END_TEST
static int
_inherit_value_1(Eo *obj EINA_UNUSED, void *pd EINA_UNUSED)
{
return 1;
}
static int
_inherit_value_2(Eo *obj EINA_UNUSED, void *pd EINA_UNUSED)
{
return 2;
}
EFL_FUNC_BODY(inherit_value, int, 0);
static Eina_Bool
_cast_inherit_class_initializer_1(Efl_Class *klass)
{
EFL_OPS_DEFINE(ops, EFL_OBJECT_OP_FUNC(inherit_value, _inherit_value_1), );
return efl_class_functions_set(klass, &ops, NULL);
}
static Eina_Bool
_cast_inherit_class_initializer_2(Efl_Class *klass)
{
EFL_OPS_DEFINE(ops, EFL_OBJECT_OP_FUNC(inherit_value, _inherit_value_2), );
return efl_class_functions_set(klass, &ops, NULL);
}
EFL_START_TEST(efl_cast_test)
{
static const Efl_Class_Description class_desc_1 = {
EO_VERSION,
"FirstInherit",
EFL_CLASS_TYPE_REGULAR,
0,
_cast_inherit_class_initializer_1,
NULL,
NULL
};
static const Efl_Class_Description class_desc_2 = {
EO_VERSION,
"SecondInherit",
EFL_CLASS_TYPE_REGULAR,
0,
_cast_inherit_class_initializer_2,
NULL,
NULL
};
const Efl_Class *klass1 = efl_class_new(&class_desc_1, SIMPLE_CLASS, NULL);
fail_if(!klass1);
const Efl_Class *klass2 = efl_class_new(&class_desc_2, klass1, NULL);
fail_if(!klass2);
Eo *obj;
// Testing normal calls
obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj);
ck_assert_int_eq(inherit_value(obj), 0);
efl_unref(obj);
obj = efl_add_ref(klass1, NULL);
fail_if(!obj);
ck_assert_int_eq(inherit_value(obj), 1);
efl_unref(obj);
obj = efl_add_ref(klass2, NULL);
fail_if(!obj);
ck_assert_int_eq(inherit_value(obj), 2);
efl_unref(obj);
// Testing efl_super
obj = efl_add_ref(klass2, NULL);
fail_if(!obj);
ck_assert_int_eq(inherit_value(efl_super(obj, klass2)), 1);
ck_assert_int_eq(inherit_value(efl_super(obj, klass1)), 0);
efl_unref(obj);
// Testing efl_cast
obj = efl_add_ref(klass2, NULL);
fail_if(!obj);
ck_assert_int_eq(inherit_value(efl_cast(obj, klass2)), 2);
ck_assert_int_eq(inherit_value(efl_cast(obj, klass1)), 1);
ck_assert_int_eq(inherit_value(efl_cast(obj, SIMPLE_CLASS)), 0);
efl_unref(obj);
}
EFL_END_TEST
static void _destruct_test_del_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
int *var = data;
*var = 1;
}
static void _destruct_test_destruct_cb(void *data, const Efl_Event *ev)
{
int *var = data;
*var *= 2;
ck_assert_int_eq(efl_ref_count(ev->object), 0);
// test disabled: object isn't yet marked as destructed (we're inside the
// base class destructor here).
//ck_assert_int_ne(efl_destructed_is(ev->object), 0);
}
EFL_START_TEST(efl_object_destruct_test)
{
int var = 0;
Eo *obj;
obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(efl_ref_count(obj) != 1);
efl_event_callback_add(obj, EFL_EVENT_DEL, _destruct_test_del_cb, &var);
efl_event_callback_add(obj, EFL_EVENT_DESTRUCT, _destruct_test_destruct_cb, &var);
efl_unref(obj);
// var should be 2 if del then destruct, 0 otherwise
ck_assert_int_eq(var, 2);
}
EFL_END_TEST
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
static void
_auto_unref_del_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
*((int *) data) = 1;
}
EFL_START_TEST(efl_object_auto_unref_test)
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
{
int _auto_unref_del;
Eo *obj, *parent;
// Test unref after valid call
_auto_unref_del = 0;
obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(efl_ref_count(obj) != 1);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
efl_event_callback_add(obj, EFL_EVENT_DEL, _auto_unref_del_cb, &_auto_unref_del);
___efl_auto_unref_set(obj, 1);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
fail_if(_auto_unref_del);
fail_if(efl_ref_count(obj) != 1);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
efl_name_set(obj, "name");
fail_if(!_auto_unref_del);
// Test unref after invalid call
_auto_unref_del = 0;
obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(efl_ref_count(obj) != 1);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
efl_event_callback_add(obj, EFL_EVENT_DEL, _auto_unref_del_cb, &_auto_unref_del);
___efl_auto_unref_set(obj, 1);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
fail_if(_auto_unref_del);
fail_if(efl_ref_count(obj) != 1);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
simple_no_implementation(obj);
fail_if(!_auto_unref_del);
// Same with a parent
_auto_unref_del = 0;
parent = efl_add_ref(SIMPLE_CLASS, NULL);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
obj = efl_add(SIMPLE_CLASS, parent);
fail_if(efl_ref_count(obj) != 1);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
efl_allow_parent_unref_set(obj, 1);
efl_event_callback_add(obj, EFL_EVENT_DEL, _auto_unref_del_cb, &_auto_unref_del);
___efl_auto_unref_set(obj, 1);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
fail_if(_auto_unref_del);
fail_if(efl_ref_count(obj) != 1);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
efl_name_set(obj, "name");
fail_if(!_auto_unref_del);
efl_unref(parent);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
}
EFL_END_TEST
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
EFL_START_TEST(efl_object_size)
{
// This test is checking that we are not increasing the size of our object over time
// Update this number only if you modified the class size on purpose
#ifdef EO_DEBUG
ck_assert_int_le(efl_class_memory_size_get(SIMPLE_CLASS), 164);
#else
ck_assert_int_le(efl_class_memory_size_get(SIMPLE_CLASS), 148);
#endif
}
EFL_END_TEST
EFL_START_TEST(eo_test_class_replacement)
{
Eo *obj;
/* test basic override */
ck_assert(efl_class_override_register(SIMPLE_CLASS, SIMPLE3_CLASS));
obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj);
ck_assert_ptr_eq(efl_class_get(obj), SIMPLE3_CLASS);
efl_unref(obj);
/* test overriding with non-inheriting class */
ck_assert(!efl_class_override_register(SIMPLE_CLASS, SIMPLE2_CLASS));
/* test removing an invalid override */
ck_assert(!efl_class_override_unregister(SIMPLE_CLASS, SIMPLE2_CLASS));
/* test removing an override */
ck_assert(efl_class_override_unregister(SIMPLE_CLASS, SIMPLE3_CLASS));
obj = efl_add_ref(SIMPLE_CLASS, NULL);
fail_if(!obj);
ck_assert_ptr_eq(efl_class_get(obj), SIMPLE_CLASS);
efl_unref(obj);
}
EFL_END_TEST
EFL_START_TEST(eo_test_class_type)
{
ck_assert_int_eq(efl_class_type_get(SIMPLE_CLASS), EFL_CLASS_TYPE_REGULAR);
ck_assert_int_eq(efl_class_type_get((void*)0xAFFE), EFL_CLASS_TYPE_INVALID);
}
EFL_END_TEST
void eo_test_general(TCase *tc)
{
tcase_add_test(tc, eo_simple);
tcase_add_test(tc, eo_singleton);
tcase_add_test(tc, efl_object_override_tests);
tcase_add_test(tc, eo_test_class_replacement);
tcase_add_test(tc, eo_signals);
tcase_add_test(tc, efl_data_fetch);
tcase_add_test(tc, efl_data_safe_fetch);
tcase_add_test(tc, efl_isa_tests);
tcase_add_test(tc, efl_composite_tests);
tcase_add_test(tc, eo_man_free);
tcase_add_test(tc, efl_refs);
tcase_add_test(tc, efl_weak_reference);
tcase_add_test(tc, eo_generic_data);
tcase_add_test(tc, eo_magic_checks);
tcase_add_test(tc, efl_func_resolve);
tcase_add_test(tc, efl_add_do_and_custom);
tcase_add_test(tc, eo_pointers_indirection);
tcase_add_test(tc, efl_add_failures);
tcase_add_test(tc, efl_del_intercept);
tcase_add_test(tc, efl_name);
tcase_add_test(tc, eo_comment);
tcase_add_test(tc, eo_rec_interface);
tcase_add_test(tc, eo_domain);
tcase_add_test(tc, efl_cast_test);
tcase_add_test(tc, efl_object_destruct_test);
eo: Add beta API for auto_unref after a call Before screaming in horror (C++...) here's why we may need this: Efl.Part.part API returns an object that is by definition valid for a single function call only. Enforcing this in practice is actually quite hard as all implementation functions must manually take care of the life-cycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that life-cycle. Also, this means any invalid function call on a part will leak an object. This API absolutely must remain either "internal" or "beta" and definitely not become abused by applications. On top of that such an API can cause great trouble for bindings like C++. As a consequence, only specially crafted APIs like efl_part() should return an object marked as auto_unref. Alternatively this API could be defined in Eo.h or some other Eo_Internal.h. I placed it in efl_object.eo because it's much more convenient :) (read: I'm lazy) **** Performance notes: Tested with clang & gcc (with -O2), I had a look at the output of perf top, in particular the asm view. I used eo_bench in a loop. My conclusions are: - EINA_LIKELY/UNLIKELY actually works. The jump statement varies according to the expectation. I highly doubt all those ugly goto in eo.c / Eo.h are even useful. - The impact of auto_unref on a call_resolve is so small it doesn't even appear in the trace. It is significant inside call_end, though (obviously, that function is just a few lines long). That function accounts for ~1% to ~4% of all CPU time. The impact of auto_unref in call_end is ~4% of the function time. This means ~0.16% of all CPU time (worst measured case). _efl_object_op_api_id_get simply doesn't show up because of caching, so the extra check there is negligible. PS: I also tested EINA_LIKELY/UNLIKELY by compiling with -O2 and looking at the output with objdump. The flag is well respected, and the jump instructions are what you would expect (no jump for LIKELY and jump for UNLIKELY). Conclusion: The goto's in eo.c only make the code harder to read...
2017-10-13 01:18:41 -07:00
tcase_add_test(tc, efl_object_auto_unref_test);
tcase_add_test(tc, efl_object_size);
tcase_add_test(tc, eo_test_class_type);
}