array threadsafing wip, get rid of dumb undefineds from earlier
authordiscomfitor <discomfitor@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Tue, 10 Aug 2010 11:57:20 +0000 (11:57 +0000)
committerdiscomfitor <discomfitor@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Tue, 10 Aug 2010 11:57:20 +0000 (11:57 +0000)
git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/eina@50959 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

src/include/eina_array.h
src/lib/eina_array.c
src/tests/eina_test_array.c

index e756eae..d133d66 100644 (file)
 #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(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
@@ -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)
-
-#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
@@ -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"
 
 /**
index ef26a71..75e8303 100644 (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,13 +427,61 @@ eina_array_new(unsigned int step)
    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 @@ 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;
 }
 
index c5d118d..e0ff4b3 100644 (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)