jpeg pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=4550b4cf832b256d6bca49a5e536c2327bbd5e8c

commit 4550b4cf832b256d6bca49a5e536c2327bbd5e8c
Author: Jean-Philippe Andre <jp.an...@samsung.com>
Date:   Wed Jan 11 15:34:15 2017 +0900

    eina: Introduce Eina_Slstr for short-lived strings
    
    Built on top of the new 'postponed' free queue, the short-lived
    strings API allows users to return new strings without caring
    about freeing them. EFL main loop will do this automatically for
    them you at a later point in time (at the end of an iteration).
    
    The APIs provided will either duplicate (copy) or more generally
    steal an existing string (char *, stringshare, tmpstr, strbuf),
    taking ownership of it and controling its lifetime. Those strings
    can then be safely returned by an API. From a user point of view,
    those strings must be considered like simple const char *, ie.
    no need to free() them and their validity is limited to the
    local scope.
    
    There is no function to remove such a string from the freeq.
    
    The short lived strings API is not thread-safe: do not send a
    short-lived object from one thread to another.
    
    @feature
---
 src/Makefile_Eina.am             |   9 +-
 src/lib/ecore/ecore_main.c       |   7 ++
 src/lib/eina/Eina.h              |   1 +
 src/lib/eina/eina_freeq.h        |   3 +-
 src/lib/eina/eina_main.c         |   3 +
 src/lib/eina/eina_slstr.c        | 215 +++++++++++++++++++++++++++++++++++++++
 src/lib/eina/eina_slstr.h        | 179 ++++++++++++++++++++++++++++++++
 src/tests/eina/eina_suite.c      |   1 +
 src/tests/eina/eina_suite.h      |   1 +
 src/tests/eina/eina_test_slstr.c | 208 +++++++++++++++++++++++++++++++++++++
 10 files changed, 623 insertions(+), 4 deletions(-)

diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am
index 56b1959..ae3d94b 100644
--- a/src/Makefile_Eina.am
+++ b/src/Makefile_Eina.am
@@ -103,7 +103,8 @@ lib/eina/eina_inline_safepointer.x \
 lib/eina/eina_slice.h \
 lib/eina/eina_inline_slice.x \
 lib/eina/eina_inline_modinfo.x \
-lib/eina/eina_freeq.h
+lib/eina/eina_freeq.h \
+lib/eina/eina_slstr.h
 
 
 lib_eina_libeina_la_SOURCES = \
@@ -177,7 +178,8 @@ lib/eina/eina_strbuf_common.h \
 lib/eina/eina_quaternion.c \
 lib/eina/eina_bezier.c \
 lib/eina/eina_safepointer.c \
-lib/eina/eina_freeq.c
+lib/eina/eina_freeq.c \
+lib/eina/eina_slstr.c
 
 
 if HAVE_WIN32
@@ -348,7 +350,8 @@ tests/eina/eina_test_vector.c \
 tests/eina/eina_test_bezier.c \
 tests/eina/eina_test_safepointer.c \
 tests/eina/eina_test_slice.c \
-tests/eina/eina_test_freeq.c
+tests/eina/eina_test_freeq.c \
+tests/eina/eina_test_slstr.c
 
 tests_eina_eina_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
 -DTESTS_WD=\"`pwd`\" \
diff --git a/src/lib/ecore/ecore_main.c b/src/lib/ecore/ecore_main.c
index 6bff191..ebd272d 100644
--- a/src/lib/ecore/ecore_main.c
+++ b/src/lib/ecore/ecore_main.c
@@ -2,6 +2,8 @@
 # include <config.h>
 #endif
 
+#define EINA_SLSTR_INTERNAL
+
 #ifdef _WIN32
 # define WIN32_LEAN_AND_MEAN
 # include <winsock2.h>
@@ -2388,6 +2390,11 @@ process_all: 
/*-*********************************************************/
 done: /*-*****************************************************************/
    /* Agressively flush animator */
    _ecore_animator_flush();
+   if (!once_only)
+     {
+        /* Free all short lived strings */
+        eina_slstr_local_clear();
+     }
    in_main_loop--;
 }
 
diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h
index 374e4ec..6a99b4f 100644
--- a/src/lib/eina/Eina.h
+++ b/src/lib/eina/Eina.h
@@ -271,6 +271,7 @@ extern "C" {
 #include <eina_safepointer.h>
 #include <eina_slice.h>
 #include <eina_freeq.h>
+#include <eina_slstr.h>
 
 #undef EAPI
 #define EAPI
diff --git a/src/lib/eina/eina_freeq.h b/src/lib/eina/eina_freeq.h
index 643aa1e..7b34669 100644
--- a/src/lib/eina/eina_freeq.h
+++ b/src/lib/eina/eina_freeq.h
@@ -89,7 +89,8 @@ typedef enum _Eina_FreeQ_Type
     * immediately. Use this kind of freeq for debugging and additional memory
     * safety purposes only.
     *
-    * This type of free queue is thread-safe.
+    * As this type of free queue is thread-safe, the free functions used must
+    * also be thread-safe (eg. libc free()).
     *
     * @since 1.19
     */
diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c
index ab0aec9..c0e2825 100644
--- a/src/lib/eina/eina_main.c
+++ b/src/lib/eina/eina_main.c
@@ -69,6 +69,7 @@
 #include "eina_value.h"
 #include "eina_evlog.h"
 #include "eina_freeq.h"
+#include "eina_slstr.h"
 
 /*============================================================================*
 *                                  Local                                     *
@@ -153,6 +154,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL;
    S(rbtree);
    S(file);
    S(safepointer);
+   S(slstr);
 #undef S
 
 struct eina_desc_setup
@@ -198,6 +200,7 @@ static const struct eina_desc_setup _eina_desc_setup[] = {
    S(rbtree),
    S(file),
    S(safepointer),
+   S(slstr),
 #undef S
 };
 static const size_t _eina_desc_setup_len = sizeof(_eina_desc_setup) /
diff --git a/src/lib/eina/eina_slstr.c b/src/lib/eina/eina_slstr.c
new file mode 100644
index 0000000..5367db0
--- /dev/null
+++ b/src/lib/eina/eina_slstr.c
@@ -0,0 +1,215 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define EINA_SLSTR_INTERNAL
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Eina.h"
+#include "eina_private.h"
+
+// ========================================================================= //
+
+static int _slstr_init = 0;
+static Eina_FreeQ *_slstr_main_fq = NULL;
+static Eina_TLS _slstr_tls = 0;
+
+// ========================================================================= //
+
+#if 0
+// 2 extension ideas here: slices for short-lived raw data buffers
+EAPI Eina_Rw_Slice eina_slslice_new(size_t length); // alloc
+EAPI Eina_Rw_Slice eina_slslice_copy(Eina_Slice slice); // copies
+EAPI Eina_Rw_Slice eina_slslice_free(Eina_Rw_Slice slice); // steals
+#endif
+
+static void
+_slstr_tls_free_cb(void *ptr)
+{
+   Eina_FreeQ *fq = ptr;
+
+   eina_freeq_free(fq);
+}
+
+Eina_Bool
+eina_slstr_init(void)
+{
+   if (_slstr_init++) return EINA_TRUE;
+
+   _slstr_main_fq = eina_freeq_new(EINA_FREEQ_POSTPONED);
+   if (!_slstr_main_fq) goto fail;
+   if (!eina_tls_cb_new(&_slstr_tls, _slstr_tls_free_cb)) goto fail_tls;
+
+   return EINA_TRUE;
+
+fail_tls:
+   eina_tls_free(_slstr_tls);
+   _slstr_tls = 0;
+fail:
+   eina_freeq_free(_slstr_main_fq);
+   _slstr_main_fq = NULL;
+   return EINA_FALSE;
+}
+
+Eina_Bool
+eina_slstr_shutdown(void)
+{
+   if (_slstr_init == 0) return EINA_FALSE;
+   if (--_slstr_init) return EINA_TRUE;
+
+   eina_freeq_free(_slstr_main_fq);
+   eina_tls_free(_slstr_tls);
+   _slstr_main_fq = NULL;
+   _slstr_tls = 0;
+
+   return EINA_TRUE;
+}
+
+static inline Eina_FreeQ *
+_slstr_freeq_get(Eina_Bool nocreate)
+{
+   if (eina_main_loop_is())
+     return _slstr_main_fq;
+   else
+     {
+        Eina_FreeQ *fq;
+
+        fq = eina_tls_get(_slstr_tls);
+        if (!nocreate && EINA_UNLIKELY(!fq))
+          {
+             fq = eina_freeq_new(EINA_FREEQ_POSTPONED);
+             eina_tls_set(_slstr_tls, fq);
+          }
+        return fq;
+     }
+}
+
+EAPI Eina_Slstr *
+eina_slstr_copy_new(const char *string)
+{
+   Eina_FreeQ *fq;
+   char *copy;
+   size_t len = 0;
+
+   if (!string) return NULL;
+
+   fq = _slstr_freeq_get(EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(fq, NULL);
+
+   copy = eina_strdup(string);
+   if (!copy) return NULL;
+#ifdef DEBUG
+   len = strlen(string) + 1;
+#endif
+   eina_freeq_ptr_add(fq, copy, free, len);
+   return copy;
+}
+
+EAPI Eina_Slstr *
+eina_slstr_steal_new(char *string)
+{
+   Eina_FreeQ *fq;
+   size_t len = 0;
+
+   if (!string) return NULL;
+
+   fq = _slstr_freeq_get(EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(fq, NULL);
+
+#ifdef DEBUG
+   len = strlen(string) + 1;
+#endif
+   eina_freeq_ptr_add(fq, string, free, len);
+   return string;
+}
+
+EAPI Eina_Slstr *
+eina_slstr_stringshare_new(Eina_Stringshare *string)
+{
+   Eina_FreeQ *fq;
+   size_t len = 0;
+
+   if (!string) return NULL;
+
+   fq = _slstr_freeq_get(EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(fq, NULL);
+
+#ifdef DEBUG
+   len = eina_stringshare_strlen(string) + 1;
+#endif
+   eina_freeq_ptr_add(fq, (void *) string, EINA_FREE_CB(eina_stringshare_del), 
len);
+   return string;
+}
+
+EAPI Eina_Slstr *
+eina_slstr_tmpstr_new(Eina_Tmpstr *string)
+{
+   Eina_FreeQ *fq;
+   size_t len = 0;
+
+   if (!string) return NULL;
+
+   fq = _slstr_freeq_get(EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(fq, NULL);
+
+#ifdef DEBUG
+   len = eina_tmpstr_strlen(string) + 1;
+#endif
+   eina_freeq_ptr_add(fq, (void *) string, EINA_FREE_CB(eina_tmpstr_del), len);
+   return string;
+}
+
+EAPI Eina_Slstr *
+eina_slstr_strbuf_new(Eina_Strbuf *string)
+{
+   Eina_FreeQ *fq;
+   size_t len = 0;
+   char *str;
+
+   if (!string) return NULL;
+
+   fq = _slstr_freeq_get(EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(fq, NULL);
+
+   str = eina_strbuf_release(string);
+#ifdef DEBUG
+   len = eina_strbuf_length_get(string) + 1;
+#endif
+   eina_freeq_ptr_add(fq, str, free, len);
+   return str;
+}
+
+EAPI Eina_Slstr *
+eina_slstr_vasprintf_new(const char *fmt, va_list args)
+{
+   Eina_FreeQ *fq;
+   size_t len = 0;
+   char *str;
+   int r;
+
+   fq = _slstr_freeq_get(EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(fq, NULL);
+
+   r = vasprintf(&str, fmt, args);
+   if (r == -1) return NULL;
+
+#ifdef DEBUG
+   len = r + 1;
+#endif
+   eina_freeq_ptr_add(fq, str, free, len);
+   return str;
+}
+
+EAPI void
+eina_slstr_local_clear(void)
+{
+   Eina_FreeQ *fq;
+
+   fq = _slstr_freeq_get(EINA_TRUE);
+   if (!fq) return;
+
+   eina_freeq_clear(fq);
+}
diff --git a/src/lib/eina/eina_slstr.h b/src/lib/eina/eina_slstr.h
new file mode 100644
index 0000000..0e5d66e
--- /dev/null
+++ b/src/lib/eina/eina_slstr.h
@@ -0,0 +1,179 @@
+#ifndef EINA_SLSTR_H_
+#define EINA_SLSTR_H_
+
+#include <stdlib.h>
+
+#include "eina_config.h"
+
+#include "eina_types.h"
+#include "eina_tmpstr.h"
+#include "eina_strbuf.h"
+#include "eina_stringshare.h"
+#include "eina_slice.h"
+
+/**
+ * @addtogroup Eina_Slstr Short lived strings
+ * @ingroup Eina
+ *
+ * @brief API for short lived strings (thread- and scope-local)
+ *
+ * This set of APIs provide a convenience feature to create and return strings
+ * that are meant to be consumed in the local scope of the calling code block.
+ * The lifecycle of those strings is bound to the loop of the current thread
+ * or until the clear function is called explicitely.
+ *
+ * These strings will be automatically deleted.
+ *
+ * These functions shall return NULL only if out of memory.
+ *
+ * Do not call free or any similar function on a string created with this API!
+ *
+ * @since 1.19
+ */
+
+typedef const char Eina_Slstr;
+
+/**
+ * @brief Create a new short lived string by duplicating another string.
+ *
+ * @param string An existing string, it will be copied.
+ * @return A new Eina_Slstr or NULL if out of memory.
+ *
+ * Usage example:
+ * @code
+ * char local[200];
+ * sprintf(local, "Hello %d", value);
+ * return eina_slstr_copy_new(local);
+ * @endcode
+ *
+ * @since 1.19
+ */
+EAPI Eina_Slstr *
+eina_slstr_copy_new(const char *string);
+
+/**
+ * @brief Create a new short lived string by taking ownership of a string.
+ *
+ * @param string An existing string. It will not be duplicated.
+ * @return A new Eina_Slstr or NULL if out of memory.
+ *
+ * Usage example:
+ * @code
+ * char *local = strdup("Hello");
+ * return eina_slstr_steal_new(local);
+ * @endcode
+ *
+ * @since 1.19
+ */
+EAPI Eina_Slstr *
+eina_slstr_steal_new(char *string);
+
+/**
+ * @brief Create a new short lived string by taking ownership of a stringshare.
+ *
+ * @param string An existing stringshare, one reference belongs to this slstr.
+ * @return A new Eina_Slstr or NULL if out of memory.
+ *
+ * Usage example:
+ * @code
+ * Eina_Stringshare *local = eina_stringshare_add("Hello");
+ * return eina_slstr_stringshare_new(local);
+ * @endcode
+ *
+ * @since 1.19
+ */
+EAPI Eina_Slstr *
+eina_slstr_stringshare_new(Eina_Stringshare *string);
+
+/**
+ * @brief Create a new short lived string by taking ownership of a tmpstr.
+ *
+ * @param string An existing tmpstr, it will be freed later.
+ * @return A new Eina_Slstr or NULL if out of memory.
+ *
+ * Usage example:
+ * @code
+ * Eina_Tmpstr *local = eina_tmpstr_add("Hello");
+ * return eina_slstr_tmpstr_new(local);
+ * @endcode
+ *
+ * @since 1.19
+ */
+EAPI Eina_Slstr *
+eina_slstr_tmpstr_new(Eina_Tmpstr *string);
+
+/**
+ * @brief Create a new short lived string by taking ownership of a strbuf.
+ *
+ * @param string An existing strbuf, that will be released (ie. steal + free).
+ * @return A new Eina_Slstr or NULL if out of memory.
+ *
+ * Usage example:
+ * @code
+ * Eina_Strbuf *local = eina_strbuf_new();
+ * eina_strbuf_append(local, "Hello");
+ * eina_strbuf_append(local, " world");
+ * return eina_slstr_strbuf_new(local);
+ * @endcode
+ *
+ * @note Use eina_slstr_steal_new() if the strbuf will be used after this call.
+ *
+ * @since 1.19
+ */
+EAPI Eina_Slstr *
+eina_slstr_strbuf_new(Eina_Strbuf *string);
+
+/**
+ * @brief Create a new short lived string using sprintf.
+ *
+ * @param fmt Format string for printf
+ * @param args List of format parameters for printf
+ * @return A new Eina_Slstr or NULL if out of memory.
+ *
+ * @since 1.19
+ */
+EAPI Eina_Slstr *
+eina_slstr_vasprintf_new(const char *fmt, va_list args);
+
+/**
+ * @brief Create a new short lived string using sprintf.
+ *
+ * @param fmt Format string for printf
+ * @param args List of format parameters for printf
+ * @return A new Eina_Slstr or NULL if out of memory.
+ *
+ * Usage example:
+ * @code
+ * return eina_slstr_printf("Hello world %d!", 42);
+ * @endcode
+ *
+ * @since 1.19
+ */
+static inline Eina_Slstr *
+eina_slstr_printf(const char *fmt, ...)
+{
+   Eina_Slstr *str;
+   va_list args;
+
+   va_start(args, fmt);
+   str = eina_slstr_vasprintf_new(fmt, args);
+   va_end(args);
+
+   return str;
+}
+
+#ifdef EINA_SLSTR_INTERNAL
+/**
+ * @brief Internal function to clear the strings.
+ *
+ * This internal function will be called by the local thread's loop to free
+ * all the strings. Do not call this function unless you are absolutely certain
+ * that no string in the queue will be used after this point.
+ *
+ * @since 1.19
+ */
+EAPI void
+eina_slstr_local_clear(void);
+#endif
+
+#endif
diff --git a/src/tests/eina/eina_suite.c b/src/tests/eina/eina_suite.c
index 5cb67f9..e7e1b62 100644
--- a/src/tests/eina/eina_suite.c
+++ b/src/tests/eina/eina_suite.c
@@ -86,6 +86,7 @@ static const Efl_Test_Case etc[] = {
    { "Slice", eina_test_slice },
    { "Free Queue", eina_test_freeq },
    { "Util", eina_test_util },
+   { "Short Lived Strings", eina_test_slstr },
    { NULL, NULL }
 };
 
diff --git a/src/tests/eina/eina_suite.h b/src/tests/eina/eina_suite.h
index 6f46580..7bf643e 100644
--- a/src/tests/eina/eina_suite.h
+++ b/src/tests/eina/eina_suite.h
@@ -73,5 +73,6 @@ void eina_test_bezier(TCase *tc);
 void eina_test_safepointer(TCase *tc);
 void eina_test_slice(TCase *tc);
 void eina_test_freeq(TCase *tc);
+void eina_test_slstr(TCase *tc);
 
 #endif /* EINA_SUITE_H_ */
diff --git a/src/tests/eina/eina_test_slstr.c b/src/tests/eina/eina_test_slstr.c
new file mode 100644
index 0000000..d7778a2
--- /dev/null
+++ b/src/tests/eina/eina_test_slstr.c
@@ -0,0 +1,208 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define EINA_SLSTR_INTERNAL
+#include <Eina.h>
+
+#include "eina_suite.h"
+
+static Eina_Slstr *
+_slstr_copy(void)
+{
+   const char local[] = "Hello world 1";
+
+   return eina_slstr_copy_new(local);
+}
+
+START_TEST(slstr_copy)
+{
+   Eina_Slstr *str;
+
+   eina_init();
+
+   str = _slstr_copy();
+   ck_assert_str_eq(str, "Hello world 1");
+
+   eina_shutdown();
+}
+END_TEST
+
+static Eina_Slstr *
+_slstr_steal(void)
+{
+   char *local = strdup("Hello world 2");
+
+   return eina_slstr_copy_new(local);
+}
+
+START_TEST(slstr_steal)
+{
+   Eina_Slstr *str;
+
+   eina_init();
+
+   str = _slstr_steal();
+   ck_assert_str_eq(str, "Hello world 2");
+
+   eina_shutdown();
+}
+END_TEST
+
+static Eina_Slstr *
+_slstr_stringshare(void)
+{
+   Eina_Stringshare *str = eina_stringshare_add("Hello world 3");
+
+   return eina_slstr_stringshare_new(str);
+}
+
+START_TEST(slstr_stringshare)
+{
+   Eina_Stringshare *ss;
+   Eina_Slstr *str;
+
+   eina_init();
+
+   str = _slstr_stringshare();
+   ss = eina_stringshare_add("Hello world 3");
+   fail_if(ss != str);
+
+   eina_shutdown();
+}
+END_TEST
+
+static Eina_Slstr *
+_slstr_tmpstr(void)
+{
+   Eina_Tmpstr *str = eina_tmpstr_add("Hello world 4");
+
+   return eina_slstr_tmpstr_new(str);
+}
+
+START_TEST(slstr_tmpstr)
+{
+   Eina_Slstr *str;
+
+   eina_init();
+
+   str = _slstr_tmpstr();
+   ck_assert_str_eq(str, "Hello world 4");
+
+   eina_shutdown();
+}
+END_TEST
+
+static Eina_Slstr *
+_slstr_strbuf(void)
+{
+   Eina_Strbuf *str = eina_strbuf_new();
+
+   eina_strbuf_append(str, "Hello ");
+   eina_strbuf_append(str, "world ");
+   eina_strbuf_append_printf(str, "%d", 5);
+
+   return eina_slstr_strbuf_new(str);
+}
+
+START_TEST(slstr_strbuf)
+{
+   Eina_Slstr *str;
+
+   eina_init();
+
+   str = _slstr_strbuf();
+   ck_assert_str_eq(str, "Hello world 5");
+
+   eina_shutdown();
+}
+END_TEST
+
+static Eina_Slstr *
+_slstr_printf(int val)
+{
+   return eina_slstr_printf("Hello %s %d", "world", val);
+}
+
+START_TEST(slstr_slstr_printf)
+{
+   Eina_Slstr *str;
+
+   eina_init();
+
+   str = _slstr_printf(6);
+   ck_assert_str_eq(str, "Hello world 6");
+
+   eina_shutdown();
+}
+END_TEST
+
+static void
+_many_do(void)
+{
+   const int many = 2048;
+   Eina_Slstr *str;
+   int k;
+
+   for (k = 0; k < many; k++)
+     {
+        char local[64];
+
+        str = _slstr_printf(k);
+        sprintf(local, "Hello world %d", k);
+        ck_assert_str_eq(str, local);
+     }
+}
+
+START_TEST(slstr_many)
+{
+   eina_init();
+
+   _many_do();
+
+   eina_slstr_local_clear();
+
+   eina_shutdown();
+}
+END_TEST
+
+static void *
+_thread_cb(void *data EINA_UNUSED, Eina_Thread th EINA_UNUSED)
+{
+   _many_do();
+
+   return NULL;
+}
+
+START_TEST(slstr_thread)
+{
+   const int threads = 8;
+   Eina_Thread th[threads];
+   int k;
+
+   eina_init();
+
+   for (k = 0; k < threads; k++)
+     fail_if(!eina_thread_create(&th[k], EINA_THREAD_NORMAL, -1, _thread_cb, 
NULL));
+
+   for (k = 0; k < threads; k++)
+     eina_thread_join(th[k]);
+
+   eina_slstr_local_clear();
+
+   eina_shutdown();
+}
+END_TEST
+
+void
+eina_test_slstr(TCase *tc)
+{
+   tcase_add_test(tc, slstr_copy);
+   tcase_add_test(tc, slstr_steal);
+   tcase_add_test(tc, slstr_stringshare);
+   tcase_add_test(tc, slstr_tmpstr);
+   tcase_add_test(tc, slstr_strbuf);
+   tcase_add_test(tc, slstr_slstr_printf);
+   tcase_add_test(tc, slstr_many);
+   tcase_add_test(tc, slstr_thread);
+}

-- 


Reply via email to