diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am index 56b195910f..ae3d94ba2f 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 6bff19156f..ebd272d669 100644 --- a/src/lib/ecore/ecore_main.c +++ b/src/lib/ecore/ecore_main.c @@ -2,6 +2,8 @@ # include #endif +#define EINA_SLSTR_INTERNAL + #ifdef _WIN32 # define WIN32_LEAN_AND_MEAN # include @@ -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 374e4ecfc2..6a99b4fe68 100644 --- a/src/lib/eina/Eina.h +++ b/src/lib/eina/Eina.h @@ -271,6 +271,7 @@ extern "C" { #include #include #include +#include #undef EAPI #define EAPI diff --git a/src/lib/eina/eina_freeq.h b/src/lib/eina/eina_freeq.h index 643aa1e5ce..7b34669452 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 ab0aec90b1..c0e2825151 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 0000000000..5367db010f --- /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 +#include +#include + +#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 0000000000..0e5d66e310 --- /dev/null +++ b/src/lib/eina/eina_slstr.h @@ -0,0 +1,179 @@ +#ifndef EINA_SLSTR_H_ +#define EINA_SLSTR_H_ + +#include + +#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 5cb67f909e..e7e1b623d9 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 6f46580aa5..7bf643e478 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 0000000000..d7778a23bc --- /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 + +#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); +}