Eo: Implement the fallback eo_add implementation.
authorTom Hacohen <tom@stosb.com>
Tue, 29 Mar 2016 13:47:22 +0000 (14:47 +0100)
committerTom Hacohen <tom@stosb.com>
Tue, 29 Mar 2016 15:01:52 +0000 (16:01 +0100)
The current eo_add uses a (very useful) gcc extension that is only
available in gcc compatible compilers (e.g clang). Until this commit we
just temporarily ignored this fact. This adds a fallback implementation that
can be used interchangeably with the non portable one. This means that the
same binary can call either at any point in time and the code will work.

Breaks ABI.

src/Makefile_Eo.am
src/lib/eo/Eo.h
src/lib/eo/eo.c
src/lib/eo/eo_add_fallback.c [new file with mode: 0644]
src/lib/eo/eo_add_fallback.h [new file with mode: 0644]

index e388318..c8fbd52 100644 (file)
@@ -31,6 +31,8 @@ lib/eo/eo_ptr_indirection.c \
 lib/eo/eo_ptr_indirection.h \
 lib/eo/eo_base_class.c \
 lib/eo/eo_class_class.c \
+lib/eo/eo_add_fallback.c \
+lib/eo/eo_add_fallback.h \
 lib/eo/eo_private.h
 
 lib_eo_libeo_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl @EO_CFLAGS@
@@ -58,6 +60,7 @@ tests/eo/test_interface \
 tests/eo/test_mixin \
 tests/eo/test_signals \
 tests/eo/test_children \
+tests/eo/eo_suite_add_fallback \
 tests/eo/eo_suite
 
 tests_eo_test_children_SOURCES = \
@@ -129,15 +132,25 @@ tests/eo/suite/eo_test_general.c \
 tests/eo/suite/eo_test_value.c \
 tests/eo/suite/eo_test_threaded_calls.c \
 tests/eo/suite/eo_test_init.c
+
 tests_eo_eo_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
 -DTESTS_BUILD_DIR=\"$(top_builddir)/src/tests/eo\" \
 @CHECK_CFLAGS@ \
 @EO_CFLAGS@
-TESTS += tests/eo/eo_suite
 
 tests_eo_eo_suite_LDADD = @CHECK_LIBS@ @USE_EO_LIBS@
 tests_eo_eo_suite_DEPENDENCIES = @USE_EO_INTERNAL_LIBS@
 
+TESTS += tests/eo/eo_suite
+
+tests_eo_eo_suite_add_fallback_SOURCES = $(tests_eo_eo_suite_SOURCES)
+tests_eo_eo_suite_add_fallback_CPPFLAGS = $(tests_eo_eo_suite_CPPFLAGS) \
+                                          -D_EO_ADD_FALLBACK_FORCE=1
+tests_eo_eo_suite_add_fallback_LDADD = $(tests_eo_eo_suite_LDADD)
+tests_eo_eo_suite_add_fallback_DEPENDENCIES = $(tests_eo_eo_suite_DEPENDENCIES)
+
+TESTS += tests/eo/eo_suite_add_fallback
+
 tests_eo_test_function_overrides_SOURCES = \
 tests/eo/function_overrides/function_overrides_inherit.c \
 tests/eo/function_overrides/function_overrides_inherit.h \
index 13d8283..dc6aa0e 100644 (file)
@@ -582,7 +582,7 @@ EAPI Eina_Bool _eo_call_resolve(Eo *obj, const char *func_name, Eo_Op_Call_Data
 EAPI void _eo_call_end(Eo_Op_Call_Data *call);
 
 // end of the eo_add. Calls finalize among others
-EAPI Eo * _eo_add_end(Eo *obj);
+EAPI Eo * _eo_add_end(Eo *obj, Eina_Bool is_fallback);
 
 EAPI Eo *eo_super(const Eo *obj, const Eo_Class *cur_klass);
 
@@ -597,15 +597,33 @@ EAPI Eo *eo_super(const Eo *obj, const Eo_Class *cur_klass);
  */
 EAPI const Eo_Class *eo_class_get(const Eo *obj);
 
-#define eo_self __eo_self
+EAPI Eo *_eo_self_get(void);
 
-#define _eo_add_common(klass, parent, is_ref, ...) \
+/* Check if GCC compatible (both GCC and clang define this) */
+#if defined(__GNUC__) && !defined(_EO_ADD_FALLBACK_FORCE)
+
+# define eo_self __eo_self
+
+# define _eo_add_common(klass, parent, is_ref, ...) \
    ({ \
-     Eo * const __eo_self = _eo_add_internal_start(__FILE__, __LINE__, klass, parent, is_ref); \
+     Eo * const __eo_self = _eo_add_internal_start(__FILE__, __LINE__, klass, parent, is_ref, EINA_FALSE); \
      __VA_ARGS__; \
-     (Eo *) _eo_add_end(eo_self); \
+     (Eo *) _eo_add_end(eo_self, EINA_FALSE); \
     })
 
+#else
+
+# define eo_self _eo_self_get()
+
+# define _eo_add_common(klass, parent, is_ref, ...) \
+   ( \
+     _eo_add_internal_start(__FILE__, __LINE__, klass, parent, is_ref, EINA_TRUE), \
+     ##__VA_ARGS__, \
+     (Eo *) _eo_add_end(eo_self, EINA_TRUE) \
+   )
+
+#endif
+
 /**
  * @def eo_add
  * @brief Create a new object and call its constructor(If it exits).
@@ -644,7 +662,7 @@ EAPI const Eo_Class *eo_class_get(const Eo *obj);
  */
 #define eo_add_ref(klass, parent, ...) _eo_add_common(klass, parent, EINA_TRUE, ##__VA_ARGS__)
 
-EAPI Eo * _eo_add_internal_start(const char *file, int line, const Eo_Class *klass_id, Eo *parent, Eina_Bool ref);
+EAPI Eo * _eo_add_internal_start(const char *file, int line, const Eo_Class *klass_id, Eo *parent, Eina_Bool ref, Eina_Bool is_fallback);
 
 /**
  * @brief Get a pointer to the data of an object for a specific class.
index ba12fbd..434ab3e 100644 (file)
@@ -13,6 +13,7 @@
 #include "Eo.h"
 #include "eo_ptr_indirection.h"
 #include "eo_private.h"
+#include "eo_add_fallback.h"
 
 #define EO_CLASS_IDS_FIRST 1
 #define EO_OP_IDS_FIRST 1
@@ -615,9 +616,15 @@ _eo_class_funcs_set(_Eo_Class *klass)
 }
 
 EAPI Eo *
-_eo_add_internal_start(const char *file, int line, const Eo_Class *klass_id, Eo *parent_id, Eina_Bool ref)
+_eo_add_internal_start(const char *file, int line, const Eo_Class *klass_id, Eo *parent_id, Eina_Bool ref, Eina_Bool is_fallback)
 {
    _Eo_Object *obj;
+   Eo_Stack_Frame *fptr = NULL;
+
+   if (is_fallback)
+     {
+        fptr = _eo_add_fallback_stack_push(NULL);
+     }
 
    EO_CLASS_POINTER_RETURN_VAL(klass_id, klass, NULL);
 
@@ -680,6 +687,11 @@ _eo_add_internal_start(const char *file, int line, const Eo_Class *klass_id, Eo
         return NULL;
      }
 
+   if (is_fallback)
+     {
+        fptr->obj = eo_id;
+     }
+
    return eo_id;
 }
 
@@ -726,11 +738,16 @@ cleanup:
 }
 
 EAPI Eo *
-_eo_add_end(Eo *eo_id)
+_eo_add_end(Eo *eo_id, Eina_Bool is_fallback)
 {
    Eo *ret = eo_finalize(eo_id);
    ret = _eo_add_internal_end(eo_id, ret);
 
+   if (is_fallback)
+     {
+        _eo_add_fallback_stack_pop();
+     }
+
    return ret;
 }
 
@@ -1595,6 +1612,8 @@ eo_init(void)
    _eo_class_isa_func(NULL, NULL, NULL);
 #endif
 
+   _eo_add_fallback_init();
+
    eina_log_timing(_eo_log_dom,
                    EINA_LOG_STATE_STOP,
                    EINA_LOG_STATE_INIT);
@@ -1618,6 +1637,8 @@ eo_shutdown(void)
                    EINA_LOG_STATE_START,
                    EINA_LOG_STATE_SHUTDOWN);
 
+   _eo_add_fallback_shutdown();
+
    for (i = 0 ; i < _eo_classes_last_id ; i++, cls_itr++)
      {
         if (*cls_itr)
diff --git a/src/lib/eo/eo_add_fallback.c b/src/lib/eo/eo_add_fallback.c
new file mode 100644 (file)
index 0000000..6d714d6
--- /dev/null
@@ -0,0 +1,188 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined HAVE_DLADDR && ! defined _WIN32
+# include <dlfcn.h>
+#endif
+
+#include <Eina.h>
+
+#include "eo_ptr_indirection.h"
+#include "eo_private.h"
+
+#include "eo_add_fallback.h"
+
+// 1024 entries == 16k or 32k (32 or 64bit) for eo call stack. that's 1023
+// imbricated/recursive calls it can handle before barfing. i'd say that's ok
+#define EO_CALL_STACK_DEPTH_MIN 1024
+
+typedef struct _Eo_Call_Stack {
+   Eo_Stack_Frame *frames;
+   Eo_Stack_Frame *frame_ptr;
+} Eo_Call_Stack;
+
+#define EO_CALL_STACK_SIZE (EO_CALL_STACK_DEPTH_MIN * sizeof(Eo_Stack_Frame))
+
+static Eina_TLS _eo_call_stack_key = 0;
+
+#define MEM_PAGE_SIZE 4096
+
+static void *
+_eo_call_stack_mem_alloc(size_t size)
+{
+#ifdef HAVE_MMAP
+   // allocate eo call stack via mmped anon segment if on linux - more
+   // secure and safe. also gives page aligned memory allowing madvise
+   void *ptr;
+   size_t newsize;
+   newsize = MEM_PAGE_SIZE * ((size + MEM_PAGE_SIZE - 1) /
+                              MEM_PAGE_SIZE);
+   ptr = mmap(NULL, newsize, PROT_READ | PROT_WRITE,
+              MAP_PRIVATE | MAP_ANON, -1, 0);
+   if (ptr == MAP_FAILED)
+     {
+        ERR("eo call stack mmap failed.");
+        return NULL;
+     }
+   return ptr;
+#else
+   //in regular cases just use malloc
+   return calloc(1, size);
+#endif
+}
+
+static void
+_eo_call_stack_mem_free(void *ptr, size_t size)
+{
+#ifdef HAVE_MMAP
+   munmap(ptr, size);
+#else
+   (void) size;
+   free(ptr);
+#endif
+}
+
+static Eo_Call_Stack *
+_eo_call_stack_create()
+{
+   Eo_Call_Stack *stack;
+
+   stack = calloc(1, sizeof(Eo_Call_Stack));
+   if (!stack)
+     return NULL;
+
+   stack->frames = _eo_call_stack_mem_alloc(EO_CALL_STACK_SIZE);
+   if (!stack->frames)
+     {
+        free(stack);
+        return NULL;
+     }
+
+   // first frame is never used
+   stack->frame_ptr = stack->frames;
+
+   return stack;
+}
+
+static void
+_eo_call_stack_free(void *ptr)
+{
+   Eo_Call_Stack *stack = (Eo_Call_Stack *) ptr;
+
+   if (!stack) return;
+
+   if (stack->frames)
+     _eo_call_stack_mem_free(stack->frames, EO_CALL_STACK_SIZE);
+
+   free(stack);
+}
+
+static Eo_Call_Stack *main_loop_stack = NULL;
+
+#define _EO_CALL_STACK_GET() ((EINA_LIKELY(eina_main_loop_is())) ? main_loop_stack : _eo_call_stack_get_thread())
+
+static inline Eo_Call_Stack *
+_eo_call_stack_get_thread(void)
+{
+   Eo_Call_Stack *stack = eina_tls_get(_eo_call_stack_key);
+
+   if (stack) return stack;
+
+   stack = _eo_call_stack_create();
+   eina_tls_set(_eo_call_stack_key, stack);
+
+   return stack;
+}
+
+EAPI Eo *
+_eo_self_get(void)
+{
+   return _EO_CALL_STACK_GET()->frame_ptr->obj;
+}
+
+Eo_Stack_Frame *
+_eo_add_fallback_stack_push(Eo *obj)
+{
+   Eo_Call_Stack *stack = _EO_CALL_STACK_GET();
+   if (stack->frame_ptr == (stack->frames + EO_CALL_STACK_DEPTH_MIN))
+     {
+        CRI("eo_add fallback stack overflow.");
+     }
+
+   stack->frame_ptr++;
+   stack->frame_ptr->obj = obj;
+
+   return stack->frame_ptr;
+}
+
+Eo_Stack_Frame *
+_eo_add_fallback_stack_pop(void)
+{
+   Eo_Call_Stack *stack = _EO_CALL_STACK_GET();
+   if (stack->frame_ptr == stack->frames)
+     {
+        CRI("eo_add fallback stack underflow.");
+     }
+
+   stack->frame_ptr--;
+
+   return stack->frame_ptr;
+}
+
+Eina_Bool
+_eo_add_fallback_init(void)
+{
+   if (_eo_call_stack_key != 0)
+     WRN("_eo_call_stack_key already set, this should not happen.");
+   else
+     {
+        if (!eina_tls_cb_new(&_eo_call_stack_key, _eo_call_stack_free))
+          {
+             EINA_LOG_ERR("Could not create TLS key for call stack.");
+             return EINA_FALSE;
+
+          }
+     }
+
+   main_loop_stack = _eo_call_stack_create();
+   if (!main_loop_stack)
+     {
+        EINA_LOG_ERR("Could not alloc eo call stack.");
+        return EINA_FALSE;
+     }
+
+   return EINA_TRUE;
+}
+
+Eina_Bool
+_eo_add_fallback_shutdown(void)
+{
+   if (_eo_call_stack_key != 0)
+     {
+        eina_tls_free(_eo_call_stack_key);
+        _eo_call_stack_key = 0;
+     }
+
+   return EINA_TRUE;
+}
diff --git a/src/lib/eo/eo_add_fallback.h b/src/lib/eo/eo_add_fallback.h
new file mode 100644 (file)
index 0000000..4b5e4a5
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _EO_ADD_FALLBACK_H
+#define _EO_ADD_FALLBACK_H
+
+#include <Eina.h>
+#include <Eo.h>
+
+typedef struct _Eo_Stack_Frame
+{
+   Eo *obj;
+} Eo_Stack_Frame;
+
+Eina_Bool _eo_add_fallback_init(void);
+Eina_Bool _eo_add_fallback_shutdown(void);
+
+Eo_Stack_Frame *_eo_add_fallback_stack_push(Eo *obj);
+Eo_Stack_Frame *_eo_add_fallback_stack_pop(void);
+
+#endif