#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
*
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);
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(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;
+}
-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;
+static inline Eina_Bool
+eina_array_unlock(Eina_Array *array)
+{
+ if (!array) return EINA_FALSE;
+ return EINA_TRUE;
+}
+#endif
/**
* @def EINA_ARRAY_ITER_NEXT
(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.
* @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)
-
-#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
+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)
-#ifdef EINA_RWLOCKS_ENABLED
/**
* @def EINA_ARRAY_THREADSAFE_ITER_ESCAPE
* @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.
#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"
/**
{
Eina_Iterator iterator;
- Eina_Array *array;
+ const Eina_Array *array;
unsigned int index;
EINA_MAGIC
struct _Eina_Accessor_Array
{
Eina_Accessor accessor;
- Eina_Array *array;
+ const Eina_Array *array;
EINA_MAGIC
};
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);
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 *
*============================================================================*/
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
}
/**
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);
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);
}
/**
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);
}
/**
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;
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);
}
/**
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)
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;
}
* 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;
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);
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;
}
* 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;
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);
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;
}