array threadsafing wip, get rid of dumb undefineds from earlier

SVN revision: 50959
This commit is contained in:
Mike Blumenkrantz 2010-08-10 11:57:20 +00:00
parent 10a14f3994
commit cbf7f7b58a
3 changed files with 168 additions and 130 deletions

View File

@ -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 @@ 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"
/**

View File

@ -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;
}

View File

@ -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)