summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Philippe Andre <jp.andre@samsung.com>2017-10-13 17:18:41 +0900
committerJean-Philippe Andre <jp.andre@samsung.com>2017-10-13 18:02:20 +0900
commite91af478ef76d1498c529179e99ea7d8d71ca3cb (patch)
tree558176e8f163ee364afebdcfa6c8e70c7c716fef
parent00ba7b1b6fe1f745da5cd2d716988aea6844ae05 (diff)
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 lifecycle. This is a lot of code in many places and a lot of opportunities to forget to properly handle that lifecycle. 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 :) @feature
-rw-r--r--src/lib/eo/efl_object.eo23
-rw-r--r--src/lib/eo/eo.c26
-rw-r--r--src/lib/eo/eo_base_class.c11
-rw-r--r--src/lib/eo/eo_private.h1
-rw-r--r--src/tests/eo/suite/eo_test_general.c57
5 files changed, 115 insertions, 3 deletions
diff --git a/src/lib/eo/efl_object.eo b/src/lib/eo/efl_object.eo
index b1595081b4..ceef80e08c 100644
--- a/src/lib/eo/efl_object.eo
+++ b/src/lib/eo/efl_object.eo
@@ -279,6 +279,29 @@ abstract Efl.Object ()
279 even if @.parent is not $null.]] 279 even if @.parent is not $null.]]
280 } 280 }
281 } 281 }
282 @property auto_unref @protected @beta {
283 [[Mark an object to be automatically deleted after a function call.
284
285 This becomes effectives only after finalize is done. After any EO
286 function has been called on this object, it will be unref'ed. This
287 property will also be reset to $false. This is intended to simplify
288 Part objects lifecycle.
289
290 Note: This applies to any EO function call, even if the object was
291 of the wrong type (call resolution failed).
292
293 This is a write-only property as reading it would unref the object,
294 and reset the flag.
295
296 Warning: This is a beta API, do not use it unless you know exactly
297 what you are doing.
298 ]]
299 set {}
300 values {
301 enable: bool(false);
302 [[If $true, unref this object after the next call.]]
303 }
304 }
282 } 305 }
283 implements { 306 implements {
284 class.constructor; 307 class.constructor;
diff --git a/src/lib/eo/eo.c b/src/lib/eo/eo.c
index f567ab755b..17da1115f9 100644
--- a/src/lib/eo/eo.c
+++ b/src/lib/eo/eo.c
@@ -16,6 +16,9 @@
16# include <mach/mach_time.h> 16# include <mach/mach_time.h>
17#endif 17#endif
18 18
19#define EFL_OBJECT_BETA
20#define EFL_OBJECT_PROTECTED
21
19#include "Eo.h" 22#include "Eo.h"
20#include "eo_ptr_indirection.h" 23#include "eo_ptr_indirection.h"
21#include "eo_private.h" 24#include "eo_private.h"
@@ -616,6 +619,11 @@ err_func_src:
616err: 619err:
617 if (is_obj) 620 if (is_obj)
618 { 621 {
622 if (EINA_UNLIKELY(obj->auto_unref != 0))
623 {
624 if (!(--obj->auto_unref))
625 efl_unref(eo_id);
626 }
619 _efl_unref(obj); 627 _efl_unref(obj);
620 _eo_obj_pointer_done((Eo_Id)eo_id); 628 _eo_obj_pointer_done((Eo_Id)eo_id);
621 } 629 }
@@ -670,6 +678,11 @@ _efl_object_call_end(Efl_Object_Op_Call_Data *call)
670{ 678{
671 if (EINA_LIKELY(!!call->obj)) 679 if (EINA_LIKELY(!!call->obj))
672 { 680 {
681 if (EINA_UNLIKELY(call->obj->auto_unref != 0))
682 {
683 if (!(--call->obj->auto_unref))
684 efl_unref(call->eo_id);
685 }
673 _efl_unref(call->obj); 686 _efl_unref(call->obj);
674 _eo_obj_pointer_done((Eo_Id)call->eo_id); 687 _eo_obj_pointer_done((Eo_Id)call->eo_id);
675 } 688 }
@@ -721,19 +734,26 @@ _efl_object_api_op_id_get(const void *api_func)
721} 734}
722 735
723EAPI Efl_Object_Op 736EAPI Efl_Object_Op
724_efl_object_op_api_id_get(const void *api_func, const Eo *obj, const char *api_func_name, const char *file, int line) 737_efl_object_op_api_id_get(const void *api_func, const Eo *eo_obj, const char *api_func_name, const char *file, int line)
725{ 738{
726 Efl_Object_Op op; 739 Efl_Object_Op op;
727 740
728#ifndef EO_DEBUG 741#ifndef EO_DEBUG
729 if (!obj) return EFL_NOOP; 742 if (!eo_obj) return EFL_NOOP;
730#endif 743#endif
731 op = _efl_object_api_op_id_get_internal(api_func); 744 op = _efl_object_api_op_id_get_internal(api_func);
732 if (op == EFL_NOOP) 745 if (op == EFL_NOOP)
733 { 746 {
747 EO_OBJ_POINTER(eo_obj, obj);
734 eina_log_print(_eo_log_dom, EINA_LOG_LEVEL_ERR, 748 eina_log_print(_eo_log_dom, EINA_LOG_LEVEL_ERR,
735 file, api_func_name, line, 749 file, api_func_name, line,
736 "Unable to resolve op for api func %p for obj=%p (%s)", api_func, obj, efl_class_name_get(obj)); 750 "Unable to resolve op for api func %p for obj=%p (%s)", api_func, eo_obj, efl_class_name_get(eo_obj));
751 if (EINA_UNLIKELY(obj->auto_unref))
752 {
753 if (!(--obj->auto_unref))
754 efl_unref(eo_obj);
755 }
756 return EFL_NOOP;
737 } 757 }
738 758
739 return op; 759 return op;
diff --git a/src/lib/eo/eo_base_class.c b/src/lib/eo/eo_base_class.c
index 7b6f915225..8eda874cb2 100644
--- a/src/lib/eo/eo_base_class.c
+++ b/src/lib/eo/eo_base_class.c
@@ -5,6 +5,9 @@
5#include <Eina.h> 5#include <Eina.h>
6#include <fnmatch.h> 6#include <fnmatch.h>
7 7
8#define EFL_OBJECT_BETA
9#define EFL_OBJECT_PROTECTED
10
8#include "Eo.h" 11#include "Eo.h"
9#include "eo_ptr_indirection.h" 12#include "eo_ptr_indirection.h"
10#include "eo_private.h" 13#include "eo_private.h"
@@ -2106,6 +2109,14 @@ _efl_object_allow_parent_unref_get(Eo *obj_id EINA_UNUSED, Efl_Object_Data *pd)
2106 return pd->allow_parent_unref; 2109 return pd->allow_parent_unref;
2107} 2110}
2108 2111
2112EOLIAN static void
2113_efl_object_auto_unref_set(Eo *obj_id EINA_UNUSED, Efl_Object_Data *pd EINA_UNUSED, Eina_Bool enable)
2114{
2115 // Write-only property
2116 EO_OBJ_POINTER(obj_id, obj);
2117 obj->auto_unref = enable ? 2 : 0;
2118}
2119
2109EOLIAN static Eo * 2120EOLIAN static Eo *
2110_efl_object_finalize(Eo *obj, Efl_Object_Data *pd EINA_UNUSED) 2121_efl_object_finalize(Eo *obj, Efl_Object_Data *pd EINA_UNUSED)
2111{ 2122{
diff --git a/src/lib/eo/eo_private.h b/src/lib/eo/eo_private.h
index 93fcba97dd..676b0fbab6 100644
--- a/src/lib/eo/eo_private.h
+++ b/src/lib/eo/eo_private.h
@@ -119,6 +119,7 @@ struct _Eo_Object
119 Eina_Bool del_triggered:1; 119 Eina_Bool del_triggered:1;
120 Eina_Bool destructed:1; 120 Eina_Bool destructed:1;
121 Eina_Bool manual_free:1; 121 Eina_Bool manual_free:1;
122 unsigned char auto_unref : 2; // unref after 1 call
122}; 123};
123 124
124/* How we search and store the implementations in classes. */ 125/* How we search and store the implementations in classes. */
diff --git a/src/tests/eo/suite/eo_test_general.c b/src/tests/eo/suite/eo_test_general.c
index 9a26ffea42..3c21e71c14 100644
--- a/src/tests/eo/suite/eo_test_general.c
+++ b/src/tests/eo/suite/eo_test_general.c
@@ -9,6 +9,9 @@
9# include <unistd.h> 9# include <unistd.h>
10#endif 10#endif
11 11
12#define EFL_OBJECT_BETA
13#define EFL_OBJECT_PROTECTED
14
12#include <Eo.h> 15#include <Eo.h>
13 16
14#include "eo_suite.h" 17#include "eo_suite.h"
@@ -1719,6 +1722,59 @@ START_TEST(efl_cast_test)
1719} 1722}
1720END_TEST 1723END_TEST
1721 1724
1725static void
1726_auto_unref_del_cb(void *data, const Efl_Event *ev EINA_UNUSED)
1727{
1728 *((int *) data) = 1;
1729}
1730
1731START_TEST(efl_object_auto_unref_test)
1732{
1733 int _auto_unref_del;
1734 Eo *obj, *parent;
1735
1736 efl_object_init();
1737
1738 // Test unref after valid call
1739 _auto_unref_del = 0;
1740 obj = efl_add(SIMPLE_CLASS, NULL);
1741 fail_if(efl_ref_get(obj) != 1);
1742 efl_event_callback_add(obj, EFL_EVENT_DEL, _auto_unref_del_cb, &_auto_unref_del);
1743 efl_auto_unref_set(obj, 1);
1744 fail_if(_auto_unref_del);
1745 fail_if(efl_ref_get(obj) != 1);
1746 efl_name_set(obj, "name");
1747 fail_if(!_auto_unref_del);
1748
1749 // Test unref after invalid call
1750 _auto_unref_del = 0;
1751 obj = efl_add(SIMPLE_CLASS, NULL);
1752 fail_if(efl_ref_get(obj) != 1);
1753 efl_event_callback_add(obj, EFL_EVENT_DEL, _auto_unref_del_cb, &_auto_unref_del);
1754 efl_auto_unref_set(obj, 1);
1755 fail_if(_auto_unref_del);
1756 fail_if(efl_ref_get(obj) != 1);
1757 simple_no_implementation(obj);
1758 fail_if(!_auto_unref_del);
1759
1760 // Same with a parent
1761 _auto_unref_del = 0;
1762 parent = efl_add(SIMPLE_CLASS, NULL);
1763 obj = efl_add(SIMPLE_CLASS, parent);
1764 fail_if(efl_ref_get(obj) != 1);
1765 efl_allow_parent_unref_set(obj, 1);
1766 efl_event_callback_add(obj, EFL_EVENT_DEL, _auto_unref_del_cb, &_auto_unref_del);
1767 efl_auto_unref_set(obj, 1);
1768 fail_if(_auto_unref_del);
1769 fail_if(efl_ref_get(obj) != 1);
1770 efl_name_set(obj, "name");
1771 fail_if(!_auto_unref_del);
1772 efl_del(parent);
1773
1774 efl_object_shutdown();
1775}
1776END_TEST
1777
1722void eo_test_general(TCase *tc) 1778void eo_test_general(TCase *tc)
1723{ 1779{
1724 tcase_add_test(tc, eo_simple); 1780 tcase_add_test(tc, eo_simple);
@@ -1744,4 +1800,5 @@ void eo_test_general(TCase *tc)
1744 tcase_add_test(tc, eo_rec_interface); 1800 tcase_add_test(tc, eo_rec_interface);
1745 tcase_add_test(tc, eo_domain); 1801 tcase_add_test(tc, eo_domain);
1746 tcase_add_test(tc, efl_cast_test); 1802 tcase_add_test(tc, efl_cast_test);
1803 tcase_add_test(tc, efl_object_auto_unref_test);
1747} 1804}