From: Enlightenment S. <no-...@en...> - 2010-08-10 11:57:27
|
Log: array threadsafing wip, get rid of dumb undefineds from earlier Author: discomfitor Date: 2010-08-10 04:57:20 -0700 (Tue, 10 Aug 2010) New Revision: 50959 Modified: trunk/eina/src/include/eina_array.h trunk/eina/src/lib/eina_array.c trunk/eina/src/tests/eina_test_array.c Modified: trunk/eina/src/include/eina_array.h =================================================================== --- trunk/eina/src/include/eina_array.h 2010-08-10 08:34:47 UTC (rev 50958) +++ trunk/eina/src/include/eina_array.h 2010-08-10 11:57:20 UTC (rev 50959) @@ -29,6 +29,11 @@ #include "eina_accessor.h" #include "eina_magic.h" +#ifdef EINA_RWLOCKS_ENABLED +# include <pthread.h> +# include <errno.h> +#endif + /** * @addtogroup Eina_Data_Types_Group Data Types * @@ -71,12 +76,15 @@ 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,13 +93,77 @@ 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 * @brief Macro to iterate over an array easily. @@ -129,7 +201,6 @@ (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,29 +241,19 @@ * @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 * @brief Macro to break a loop while using EINA_ARRAY_THREADSAFE_ITER_NEXT @@ -200,7 +261,7 @@ * @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 @@ #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" /** Modified: trunk/eina/src/lib/eina_array.c =================================================================== --- trunk/eina/src/lib/eina_array.c 2010-08-10 08:34:47 UTC (rev 50958) +++ trunk/eina/src/lib/eina_array.c 2010-08-10 11:57:20 UTC (rev 50959) @@ -169,7 +169,7 @@ { Eina_Iterator iterator; - Eina_Array *array; + const Eina_Array *array; unsigned int index; EINA_MAGIC @@ -179,7 +179,7 @@ struct _Eina_Accessor_Array { Eina_Accessor accessor; - Eina_Array *array; + const Eina_Array *array; EINA_MAGIC }; @@ -275,9 +275,10 @@ 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 @@ 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,13 +427,61 @@ array->total = 0; array->count = 0; array->step = step; + + 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); -#endif + array->threadsafe = EINA_TRUE; + DBG("array=%p", array); return array; +#else + return NULL; +#endif } /** @@ -479,17 +498,15 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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_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 @@ 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 @@ * 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_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 @@ 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 @@ * 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_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 @@ 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; } Modified: trunk/eina/src/tests/eina_test_array.c =================================================================== --- trunk/eina/src/tests/eina_test_array.c 2010-08-10 08:34:47 UTC (rev 50958) +++ trunk/eina/src/tests/eina_test_array.c 2010-08-10 11:57:20 UTC (rev 50959) @@ -34,7 +34,7 @@ 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 @@ eina_init(); - ea = eina_array_new(64); + ea = eina_array_threadsafe_new(64); fail_if(!ea); for (i = 0; i < 1000; ++i) |