diff --git a/legacy/eina/src/include/eina_array.h b/legacy/eina/src/include/eina_array.h index e756eaeb37..d133d66af9 100644 --- a/legacy/eina/src/include/eina_array.h +++ b/legacy/eina/src/include/eina_array.h @@ -29,6 +29,11 @@ #include "eina_accessor.h" #include "eina_magic.h" +#ifdef EINA_RWLOCKS_ENABLED +# include +# include +#endif + /** * @addtogroup Eina_Data_Types_Group Data Types * @@ -71,12 +76,15 @@ struct _Eina_Array unsigned int step; /**< How much must we grow the vector when it is full */ #ifdef EINA_RWLOCKS_ENABLED pthread_rwlock_t lock; + int lockcount; + Eina_Bool threadsafe:1; #endif EINA_MAGIC }; EAPI Eina_Array * eina_array_new(unsigned int step) EINA_WARN_UNUSED_RESULT EINA_MALLOC EINA_WARN_UNUSED_RESULT; +EAPI Eina_Array * eina_array_threadsafe_new(unsigned int step) EINA_WARN_UNUSED_RESULT EINA_MALLOC EINA_WARN_UNUSED_RESULT; EAPI void eina_array_free(Eina_Array *array) EINA_ARG_NONNULL(1); EAPI void eina_array_step_set(Eina_Array *array, unsigned int step) EINA_ARG_NONNULL(1); EAPI void eina_array_clean(Eina_Array *array) EINA_ARG_NONNULL(1); @@ -85,12 +93,76 @@ EAPI Eina_Bool eina_array_remove(Eina_Array *array, Eina_Bool(* static inline Eina_Bool eina_array_push(Eina_Array *array, const void *data) EINA_ARG_NONNULL(1, 2); static inline void * eina_array_pop(Eina_Array *array) EINA_ARG_NONNULL(1); -static inline void * eina_array_data_get(Eina_Array *array, unsigned int idx) EINA_ARG_NONNULL(1); -static inline void eina_array_data_set(Eina_Array *array, unsigned int idx, const void *data) EINA_ARG_NONNULL(1, 3); -static inline unsigned int eina_array_count_get(Eina_Array *array) EINA_ARG_NONNULL(1); +static inline void * eina_array_data_get(const Eina_Array *array, unsigned int idx) EINA_ARG_NONNULL(1); +static inline void eina_array_data_set(const Eina_Array *array, unsigned int idx, const void *data) EINA_ARG_NONNULL(1, 3); +static inline unsigned int eina_array_count_get(const Eina_Array *array) EINA_ARG_NONNULL(1); -EAPI Eina_Iterator * eina_array_iterator_new(Eina_Array *array) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; -EAPI Eina_Accessor * eina_array_accessor_new(Eina_Array *array) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Iterator * eina_array_iterator_new(const Eina_Array *array) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Accessor * eina_array_accessor_new(const Eina_Array *array) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +#ifdef EINA_RWLOCKS_ENABLED +static inline Eina_Bool +eina_array_rdlock(Eina_Array *array) +{ + if (!array) return EINA_FALSE; + if (array->threadsafe) + { + int ret; + + ret = pthread_rwlock_rdlock(&array->lock); + if ((ret != 0) && (ret != EDEADLK)) + return EINA_FALSE; + array->lockcount++; + } + return EINA_TRUE; +} + +static inline Eina_Bool +eina_array_wrlock(Eina_Array *array) +{ + if (!array) return EINA_FALSE; + if (array->threadsafe) + { + int ret; + + ret = pthread_rwlock_wrlock(&array->lock); + if ((ret != 0) && (ret != EDEADLK)) + return EINA_FALSE; + array->lockcount++; + } + return EINA_TRUE; +} +static inline Eina_Bool +eina_array_unlock(Eina_Array *array) +{ + if (!array) return EINA_FALSE; + if ((array->threadsafe) && (!(--array->lockcount))) + if (pthread_rwlock_unlock(&array->lock)) + return EINA_FALSE; + return EINA_TRUE; +} +#else +static inline Eina_Bool +eina_array_rdlock(Eina_Array *array) +{ + if (!array) return EINA_FALSE; + return EINA_TRUE; +} + +static inline Eina_Bool +eina_array_wrlock(Eina_Array *array) +{ + if (!array) return EINA_FALSE; + return EINA_TRUE; +} + +static inline Eina_Bool +eina_array_unlock(Eina_Array *array) +{ + if (!array) return EINA_FALSE; + return EINA_TRUE; +} +#endif /** * @def EINA_ARRAY_ITER_NEXT @@ -129,7 +201,6 @@ EAPI Eina_Accessor * eina_array_accessor_new(Eina_Array *array) EINA_ (index < eina_array_count_get(array)) && ((item = *((iterator)++))); \ ++(index)) -#ifdef EINA_RWLOCKS_ENABLED /** * @def EINA_ARRAY_THREADSAFE_ITER_NEXT * @brief Macro to iterate over an array easily while mutexing. @@ -170,28 +241,18 @@ EAPI Eina_Accessor * eina_array_accessor_new(Eina_Array *array) EINA_ * @endcode */ #define EINA_ARRAY_THREADSAFE_ITER_NEXT(array, index, item, iterator, code...) \ - if (_eina_array_threadsafety) \ - pthread_rwlock_wrlock(&(array)->lock); \ - for (index = 0, iterator = (array)->data; \ - (index < (array)->count) && ((item = *((iterator)++))); \ - ++(index)) \ - code \ - if (_eina_array_threadsafety) \ - pthread_rwlock_unlock(&(array)->lock) +do \ + { \ + if (eina_array_wrlock((Eina_Array*)array)) \ + { \ + for (index = 0, iterator = (array)->data; \ + (index < (array)->count) && ((item = *((iterator)++))); \ + ++(index)) \ + code \ + eina_array_unlock((Eina_Array*)array); \ + } \ + } while (0) -#else -#define EINA_ARRAY_THREADSAFE_ITER_NEXT(array, index, item, iterator, code...) \ - do \ - { \ - for (index = 0, iterator = (array)->data; \ - (index < (array)->count) && ((item = *((iterator)++))); \ - ++(index)) \ - code \ - } \ - while (0) -#endif - -#ifdef EINA_RWLOCKS_ENABLED /** * @def EINA_ARRAY_THREADSAFE_ITER_ESCAPE @@ -200,7 +261,7 @@ EAPI Eina_Accessor * eina_array_accessor_new(Eina_Array *array) EINA_ * @param array The array being iterated over. * @param esc The code to "escape" the loop with * - * This macro should be used any time the user wishes to leave break the loop + * This macro should be used any time the user wishes to leave the loop * while using EINA_ARRAY_THREADSAFE_ITER_NEXT. It will unlock any mutexes * which may have been locked while iterating. Failure to use this will likely * result in a deadlock. @@ -230,18 +291,11 @@ EAPI Eina_Accessor * eina_array_accessor_new(Eina_Array *array) EINA_ #define EINA_ARRAY_THREADSAFE_ITER_ESCAPE(array, esc...) \ do \ { \ - if (_eina_array_threadsafety) \ - pthread_rwlock_unlock(&(array)->lock); \ + eina_array_unlock((Eina_Array*)array); \ esc \ } \ while (0) -#else - -#define EINA_ARRAY_THREADSAFE_ITER_ESCAPE(array, esc...) \ - esc -#endif - #include "eina_inline_array.x" /** diff --git a/legacy/eina/src/lib/eina_array.c b/legacy/eina/src/lib/eina_array.c index ef26a718a0..75e8303b28 100644 --- a/legacy/eina/src/lib/eina_array.c +++ b/legacy/eina/src/lib/eina_array.c @@ -169,7 +169,7 @@ struct _Eina_Iterator_Array { Eina_Iterator iterator; - Eina_Array *array; + const Eina_Array *array; unsigned int index; EINA_MAGIC @@ -179,7 +179,7 @@ typedef struct _Eina_Accessor_Array Eina_Accessor_Array; struct _Eina_Accessor_Array { Eina_Accessor accessor; - Eina_Array *array; + const Eina_Array *array; EINA_MAGIC }; @@ -275,9 +275,10 @@ eina_array_grow(Eina_Array *array) void **tmp; unsigned int total; - EINA_MAGIC_CHECK_ARRAY(array); EINA_SAFETY_ON_NULL_RETURN_VAL(array, EINA_FALSE); + EINA_MAGIC_CHECK_ARRAY(array); + total = array->total + array->step; eina_error_set(0); tmp = realloc(array->data, sizeof (void *) * total); @@ -351,36 +352,6 @@ eina_array_shutdown(void) return EINA_TRUE; } -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK -/** - * @brief Enable threadsafe mode in arrays - * - * This function enables threadsafe mode in all arrays. - * - * @warning Once enabled, this CANNOT be disabled. - */ - -void -eina_array_threadsafety_init(void) -{ - _eina_array_threadsafety = EINA_TRUE; -} - -/** - * @brief Disable threadsafe mode in arrays - * - * This function disables threadsafe mode in all arrays. - */ - -void -eina_array_threadsafety_shutdown(void) -{ - _eina_array_threadsafety = EINA_TRUE; -} - -#endif - - /*============================================================================* * API * *============================================================================*/ @@ -456,15 +427,63 @@ eina_array_new(unsigned int step) array->total = 0; array->count = 0; array->step = step; -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - pthread_rwlock_init(&array->lock, NULL); -#endif DBG("array=%p", array); return array; } +/** + * @brief Create a new array that is threadsafe. + * + * @param step The count of pointers to add when increasing the array size. + * @return @c NULL on failure, non @c NULL otherwise. + * + * This function creates a new array which is different than a normal array. + * Arrays created with this function will automatically mutex themselves in eina_array_* + * function calls. In order to ensure that the created array operates successfully + * in a threadsafe environment, only EINA_ARRAY_THREADSAFE_* macros can be used with + * this type of list. + * When adding an element, the array allocates @p step elements. When that buffer is + * full, then adding another element will increase the buffer of @p step elements again. + * + * This function return a valid array on success, or @c NULL if memory + * allocation fails. In that case, the error is set to + * #EINA_ERROR_OUT_OF_MEMORY. + */ +EAPI Eina_Array * +eina_array_threadsafe_new(__UNUSED__ unsigned int step) +{ +#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK + Eina_Array *array; + + eina_error_set(0); + array = malloc(sizeof (Eina_Array)); + if (!array) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return NULL; + } + + EINA_MAGIC_SET(array, EINA_MAGIC_ARRAY); + + array->data = NULL; + array->total = 0; + array->count = 0; + array->step = step; + + pthread_rwlock_init(&array->lock, NULL); + array->threadsafe = EINA_TRUE; + + + DBG("array=%p", array); + + return array; +#else + return NULL; +#endif +} + /** * @brief Free an array. * @@ -479,17 +498,15 @@ eina_array_new(unsigned int step) EAPI void eina_array_free(Eina_Array *array) { -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_wrlock(&array->lock); -#endif eina_array_flush(array); - EINA_MAGIC_CHECK_ARRAY(array); EINA_SAFETY_ON_NULL_RETURN(array); + if (!eina_array_wrlock(array)) + return; + EINA_MAGIC_CHECK_ARRAY(array); DBG("array=%p", array); #ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) + if (array->threadsafe) pthread_rwlock_destroy(&array->lock); #endif MAGIC_FREE(array); @@ -509,20 +526,15 @@ EAPI void eina_array_step_set(Eina_Array *array, unsigned int step) { EINA_SAFETY_ON_NULL_RETURN(array); -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_wrlock(&array->lock); -#endif + if (!eina_array_wrlock(array)) + return; array->data = NULL; array->total = 0; array->count = 0; array->step = step; EINA_MAGIC_SET(array, EINA_MAGIC_ARRAY); DBG("array=%p, step=%u", array, step); -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_unlock(&array->lock); -#endif + eina_array_unlock(array); } /** @@ -538,17 +550,12 @@ EAPI void eina_array_clean(Eina_Array *array) { EINA_SAFETY_ON_NULL_RETURN(array); + if (!eina_array_wrlock(array)) + return; EINA_MAGIC_CHECK_ARRAY(array); -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_wrlock(&array->lock); -#endif array->count = 0; DBG("array=%p", array); -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_unlock(&array->lock); -#endif + eina_array_unlock(array); } /** @@ -565,11 +572,9 @@ EAPI void eina_array_flush(Eina_Array *array) { EINA_SAFETY_ON_NULL_RETURN(array); + if (!eina_array_wrlock(array)) + return; EINA_MAGIC_CHECK_ARRAY(array); -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_wrlock(&array->lock); -#endif DBG("array=%p", array); array->count = 0; array->total = 0; @@ -579,10 +584,7 @@ eina_array_flush(Eina_Array *array) free(array->data); array->data = NULL; -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_unlock(&array->lock); -#endif + eina_array_unlock(array); } /** @@ -619,11 +621,10 @@ eina_array_remove(Eina_Array *array, Eina_Bool (*keep)(void *data, EINA_SAFETY_ON_NULL_RETURN_VAL(array, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(keep, EINA_FALSE); + if (!eina_array_wrlock(array)) + return EINA_FALSE; EINA_MAGIC_CHECK_ARRAY(array); -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_wrlock(&array->lock); -#endif + DBG("array=%p, keep=%p, gdata=%p", array, keep, gdata); if (array->total == 0) @@ -698,10 +699,7 @@ eina_array_remove(Eina_Array *array, Eina_Bool (*keep)(void *data, array->data = tmp; array->count = total; -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_unlock(&array->lock); -#endif + eina_array_unlock(array); return EINA_TRUE; } @@ -718,7 +716,7 @@ eina_array_remove(Eina_Array *array, Eina_Bool (*keep)(void *data, * set. Otherwise, a valid iterator is returned. */ EAPI Eina_Iterator * -eina_array_iterator_new(Eina_Array *array) +eina_array_iterator_new(const Eina_Array *array) { Eina_Iterator_Array *it; @@ -736,10 +734,6 @@ eina_array_iterator_new(Eina_Array *array) EINA_MAGIC_SET(it, EINA_MAGIC_ARRAY_ITERATOR); EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_rdlock(&array->lock); -#endif it->array = array; it->iterator.next = FUNC_ITERATOR_NEXT(eina_array_iterator_next); @@ -748,10 +742,7 @@ eina_array_iterator_new(Eina_Array *array) it->iterator.free = FUNC_ITERATOR_FREE(eina_array_iterator_free); DBG("array=%p, iterator=%p", array, it); -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_unlock(&array->lock); -#endif + return &it->iterator; } @@ -768,7 +759,7 @@ eina_array_iterator_new(Eina_Array *array) * set. Otherwise, a valid accessor is returned. */ EAPI Eina_Accessor * -eina_array_accessor_new(Eina_Array *array) +eina_array_accessor_new(const Eina_Array *array) { Eina_Accessor_Array *it; @@ -786,10 +777,6 @@ eina_array_accessor_new(Eina_Array *array) EINA_MAGIC_SET(it, EINA_MAGIC_ARRAY_ACCESSOR); EINA_MAGIC_SET(&it->accessor, EINA_MAGIC_ACCESSOR); -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_rdlock(&array->lock); -#endif it->array = array; it->accessor.get_at = FUNC_ACCESSOR_GET_AT(eina_array_accessor_get_at); @@ -798,10 +785,7 @@ eina_array_accessor_new(Eina_Array *array) it->accessor.free = FUNC_ACCESSOR_FREE(eina_array_accessor_free); DBG("array=%p, accessor=%p", array, it); -#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK - if (_eina_array_threadsafety) - pthread_rwlock_unlock(&array->lock); -#endif + return &it->accessor; } diff --git a/legacy/eina/src/tests/eina_test_array.c b/legacy/eina/src/tests/eina_test_array.c index c5d118db3f..e0ff4b3744 100644 --- a/legacy/eina/src/tests/eina_test_array.c +++ b/legacy/eina/src/tests/eina_test_array.c @@ -34,7 +34,7 @@ START_TEST(eina_array_simple) eina_init(); - ea = eina_array_new(11); + ea = eina_array_threadsafe_new(11); fail_if(!ea); for (i = 0; i < 201; ++i) @@ -132,7 +132,7 @@ START_TEST(eina_array_remove_stuff) eina_init(); - ea = eina_array_new(64); + ea = eina_array_threadsafe_new(64); fail_if(!ea); for (i = 0; i < 1000; ++i)