add threadsafety for hashes
authordiscomfitor <discomfitor@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Sat, 14 Aug 2010 03:05:35 +0000 (03:05 +0000)
committerdiscomfitor <discomfitor@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Sat, 14 Aug 2010 03:05:35 +0000 (03:05 +0000)
git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/eina@51106 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

src/include/eina_hash.h
src/lib/eina_hash.c
src/tests/eina_test_hash.c

index 4e43157..ea7c2c6 100644 (file)
 #include "eina_types.h"
 #include "eina_iterator.h"
 
+#ifdef EINA_RWLOCKS_ENABLED
+# include <pthread.h>
+# include <errno.h>
+#endif
 /**
  * @addtogroup Eina_Data_Types_Group Data Types
  *
@@ -67,6 +71,15 @@ EAPI Eina_Hash *eina_hash_int64_new(Eina_Free_Cb data_free_cb);
 EAPI Eina_Hash *eina_hash_pointer_new(Eina_Free_Cb data_free_cb);
 EAPI Eina_Hash *eina_hash_stringshared_new(Eina_Free_Cb data_free_cb);
 
+EAPI Eina_Hash *eina_hash_threadsafe_new(Eina_Key_Length key_length_cb, Eina_Key_Cmp key_cmp_cb, Eina_Key_Hash key_hash_cb, Eina_Free_Cb data_free_cb, int buckets_power_size) EINA_MALLOC EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(2, 3);
+EAPI Eina_Hash *eina_hash_threadsafe_string_djb2_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *eina_hash_threadsafe_string_superfast_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *eina_hash_threadsafe_string_small_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *eina_hash_threadsafe_int32_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *eina_hash_threadsafe_int64_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *eina_hash_threadsafe_pointer_new(Eina_Free_Cb data_free_cb);
+EAPI Eina_Hash *eina_hash_threadsafe_stringshared_new(Eina_Free_Cb data_free_cb);
+
 EAPI Eina_Bool eina_hash_add(Eina_Hash *hash, const void *key, const void *data) EINA_ARG_NONNULL(1, 2, 3);
 EAPI Eina_Bool eina_hash_direct_add(Eina_Hash *hash, const void *key, const void *data) EINA_ARG_NONNULL(1, 2, 3);
 EAPI Eina_Bool eina_hash_del(Eina_Hash *hash, const void *key, const void *data) EINA_ARG_NONNULL(1);
@@ -95,6 +108,7 @@ EAPI Eina_Iterator *eina_hash_iterator_key_new(const Eina_Hash *hash) EINA_MALLO
 EAPI Eina_Iterator *eina_hash_iterator_data_new(const Eina_Hash *hash) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
 EAPI Eina_Iterator *eina_hash_iterator_tuple_new(const Eina_Hash *hash) EINA_MALLOC EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
 
+
 typedef Eina_Bool (*Eina_Hash_Foreach)(const Eina_Hash *hash, const void *key, void *data, void *fdata);
 EAPI void eina_hash_foreach(const Eina_Hash *hash, Eina_Hash_Foreach cb, const void *fdata) EINA_ARG_NONNULL(1, 2);
 
index e4fdd46..4411a05 100644 (file)
@@ -86,8 +86,9 @@ struct _Eina_Hash
 
    int population;
 
-#ifdef EINA_RWLOCKS_ENABLED
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
    pthread_rwlock_t lock;
+   Eina_Bool stay_locked:1; /* useful for iterator functions which don't want to lose lock */
    Eina_Bool threadsafe:1;
 #endif
 
@@ -107,9 +108,11 @@ struct _Eina_Hash_Element
    EINA_RBTREE;
    Eina_Hash_Tuple tuple;
    Eina_Bool begin : 1;
-#ifdef EINA_RWLOCKS_ENABLED
+#if 0 /* FIXME: implement this later for faster locking */
+# ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
    pthread_rwlock_t lock;
    int threadcount;
+# endif
 #endif
 };
 
@@ -158,6 +161,71 @@ struct _Eina_Hash_Each
                        + (uint32_t)(((const uint8_t *)(d))[0]))
 #endif
 
+#ifdef EINA_RWLOCKS_ENABLED
+static inline Eina_Bool
+eina_hash_rdlock(const Eina_Hash *hash)
+{
+   if (!hash) return EINA_FALSE;
+   if (hash->threadsafe)
+      {
+         int ret;
+
+         ret = pthread_rwlock_rdlock(&((Eina_Hash*) hash)->lock);
+         if ((ret != 0) && (ret != EDEADLK))
+                return EINA_FALSE;
+      }
+   return EINA_TRUE;
+}
+
+static inline Eina_Bool
+eina_hash_wrlock(Eina_Hash *hash)
+{
+   if (!hash) return EINA_FALSE;
+   if (hash->threadsafe)
+      {
+         int ret;
+
+         ret = pthread_rwlock_wrlock(&hash->lock);
+         if ((ret != 0) && (ret != EDEADLK))
+           return EINA_FALSE;
+      }
+   return EINA_TRUE;
+}
+
+static inline Eina_Bool
+eina_hash_unlock(const Eina_Hash *hash)
+{
+   if (!hash) return EINA_FALSE;
+   if ((hash->threadsafe) && (!hash->stay_locked))
+      if (pthread_rwlock_unlock(&((Eina_Hash*) hash)->lock))
+         return EINA_FALSE;
+   return EINA_TRUE;
+}
+
+#else
+
+static inline Eina_Bool
+eina_hash_rdlock(const Eina_Hash *hash)
+{
+   if (!hash) return EINA_FALSE;
+   return EINA_TRUE;
+}
+
+static inline Eina_Bool
+eina_hash_wrlock(Eina_Hash *hash)
+{
+   if (!hash) return EINA_FALSE;
+   return EINA_TRUE;
+}
+
+static inline Eina_Bool
+eina_hash_unlock(const Eina_Hash *hash)
+{
+   if (!hash) return EINA_FALSE;
+   return EINA_TRUE;
+}
+#endif
+
 static inline int
 _eina_hash_hash_rbtree_cmp_hash(const Eina_Hash_Head *hash_head,
                                 const int *hash,
@@ -224,10 +292,13 @@ eina_hash_add_alloc_by_hash(Eina_Hash *hash,
    Eina_Error error = 0;
    int hash_num;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(key,  EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!eina_hash_wrlock(hash))
+       return EINA_FALSE;
    error = EINA_ERROR_OUT_OF_MEMORY;
 
    /* Apply eina mask to hash. */
@@ -250,7 +321,7 @@ eina_hash_add_alloc_by_hash(Eina_Hash *hash,
 
    if (!hash_head)
      {
-        /* If not found allocate it and a element. */
+        /* If not found allocate it and an element. */
         hash_head = malloc(sizeof(Eina_Hash_Head) + sizeof(Eina_Hash_Element) + alloc_length);
         if (!hash_head)
            goto on_error;
@@ -270,7 +341,7 @@ eina_hash_add_alloc_by_hash(Eina_Hash *hash,
    if (!new_hash_element)
      {
         /*
-           Alloc every needed things
+           Alloc a new element
            (No more lookup as we expect to support more than one item for one key).
          */
         new_hash_element = malloc(sizeof (Eina_Hash_Element) + alloc_length);
@@ -297,10 +368,12 @@ eina_hash_add_alloc_by_hash(Eina_Hash *hash,
                                            _eina_hash_key_rbtree_cmp_node),
                                         (const void *)hash->key_cmp_cb);
    hash->population++;
+   eina_hash_unlock(hash);
    return EINA_TRUE;
 
 on_error:
    eina_error_set(error);
+   eina_hash_unlock(hash);
    return EINA_FALSE;
 }
 
@@ -459,9 +532,9 @@ _eina_hash_del_by_key_hash(Eina_Hash *hash,
    Eina_Hash_Head *hash_head;
    Eina_Hash_Tuple tuple;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(key,  EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
 
    if (!hash->buckets)
       return EINA_FALSE;
@@ -698,6 +771,22 @@ _eina_hash_iterator_free(Eina_Iterator_Hash *it)
       free(it);
 }
 
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+static Eina_Bool
+_eina_hash_iterator_lock(Eina_Iterator_Hash *it)
+{
+   EINA_MAGIC_CHECK_HASH_ITERATOR(it, EINA_FALSE);
+   return eina_hash_wrlock((Eina_Hash*)it->hash);
+}
+
+static Eina_Bool
+_eina_hash_iterator_unlock(Eina_Iterator_Hash *it)
+{
+   EINA_MAGIC_CHECK_HASH_ITERATOR(it, EINA_FALSE);
+   return eina_hash_unlock((Eina_Hash*)it->hash);
+}
+#endif
+
 /**
  * @endcond
  */
@@ -761,6 +850,10 @@ eina_hash_new(Eina_Key_Length key_length_cb,
    new->data_free_cb = data_free_cb;
    new->buckets = NULL;
    new->population = 0;
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+   new->threadsafe = EINA_FALSE;
+   new->stay_locked = EINA_FALSE;
+#endif
 
    new->size = 1 << buckets_power_size;
    new->mask = new->size - 1;
@@ -969,15 +1062,19 @@ eina_hash_free(Eina_Hash *hash)
    EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN(hash);
 
+   if (!eina_hash_wrlock(hash))
+    return;
    if (hash->buckets)
      {
         for (i = 0; i < hash->size; i++)
-           eina_rbtree_delete(hash->buckets[i],
-                              EINA_RBTREE_FREE_CB(_eina_hash_head_free), hash);
+           eina_rbtree_delete(hash->buckets[i], EINA_RBTREE_FREE_CB(_eina_hash_head_free), hash);
         free(hash->buckets);
      }
-
-        free(hash);
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+   if (hash->threadsafe)
+     pthread_rwlock_destroy(&hash->lock);
+#endif
+   free(hash);
 }
 
 /**
@@ -997,6 +1094,8 @@ eina_hash_free_buckets(Eina_Hash *hash)
    EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN(hash);
 
+   if (!eina_hash_wrlock(hash))
+    return;
    if (hash->buckets)
      {
         for (i = 0; i < hash->size; i++)
@@ -1006,6 +1105,7 @@ eina_hash_free_buckets(Eina_Hash *hash)
         hash->buckets = NULL;
         hash->population = 0;
      }
+   eina_hash_unlock(hash);
 }
 
 /**
@@ -1111,6 +1211,9 @@ eina_hash_add(Eina_Hash *hash, const void *key, const void *data)
    EINA_SAFETY_ON_NULL_RETURN_VAL(key,               EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(data,              EINA_FALSE);
 
+   if (!eina_hash_wrlock(hash))
+       return EINA_FALSE;
+
    key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
    key_hash = hash->key_hash_cb(key, key_length);
 
@@ -1146,11 +1249,14 @@ eina_hash_direct_add(Eina_Hash *hash, const void *key, const void *data)
    int key_length;
    int key_hash;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash,              EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(key,               EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(data,              EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!eina_hash_wrlock(hash))
+       return EINA_FALSE;
 
    key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
    key_hash = hash->key_hash_cb(key, key_length);
@@ -1182,7 +1288,10 @@ eina_hash_del_by_key_hash(Eina_Hash *hash,
 {
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(key,  EINA_FALSE);
-   return _eina_hash_del_by_key_hash(hash, key, key_length, key_hash, NULL);
+
+   if (!eina_hash_wrlock(hash))
+       return EINA_FALSE;
+   return _eina_hash_del_by_key_hash(hash, key, key_length, key_hash, NULL) && eina_hash_unlock(hash);
 }
 
 /**
@@ -1206,7 +1315,10 @@ eina_hash_del_by_key(Eina_Hash *hash, const void *key)
 {
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(key,  EINA_FALSE);
-   return _eina_hash_del_by_key(hash, key, NULL);
+
+   if (!eina_hash_wrlock(hash))
+       return EINA_FALSE;
+   return _eina_hash_del_by_key(hash, key, NULL) && eina_hash_unlock(hash);
 }
 
 /**
@@ -1230,18 +1342,25 @@ eina_hash_del_by_data(Eina_Hash *hash, const void *data)
    Eina_Hash_Head *hash_head;
    int key_hash;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!eina_hash_wrlock(hash))
+       return EINA_FALSE;
 
    hash_element = _eina_hash_find_by_data(hash, data, &key_hash, &hash_head);
    if (!hash_element)
-      return EINA_FALSE;
+      goto error;
 
    if (hash_element->tuple.data != data)
-      return EINA_FALSE;
+      goto error;
 
-   return _eina_hash_del_by_hash_el(hash, hash_element, hash_head, key_hash);
+   return _eina_hash_del_by_hash_el(hash, hash_element, hash_head, key_hash) && eina_hash_unlock(hash);
+
+error:
+   eina_hash_unlock(hash);
+   return EINA_FALSE;
 }
 
 /**
@@ -1273,12 +1392,16 @@ eina_hash_del_by_hash(Eina_Hash *hash,
                       int key_hash,
                       const void *data)
 {
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!eina_hash_wrlock(hash))
+       return EINA_FALSE;
+
    if (key)
-      return _eina_hash_del_by_key_hash(hash, key, key_length, key_hash, data);
+      return _eina_hash_del_by_key_hash(hash, key, key_length, key_hash, data) && eina_hash_unlock(hash);
    else
-      return eina_hash_del_by_data(hash, data);
+      return eina_hash_del_by_data(hash, data) && eina_hash_unlock(hash);
 }
 
 /**
@@ -1305,12 +1428,16 @@ eina_hash_del_by_hash(Eina_Hash *hash,
 EAPI Eina_Bool
 eina_hash_del(Eina_Hash *hash, const void *key, const void *data)
 {
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!eina_hash_wrlock(hash))
+       return EINA_FALSE;
+
    if (key)
-      return _eina_hash_del_by_key(hash, key, data);
+      return _eina_hash_del_by_key(hash, key, data) && eina_hash_unlock(hash);
    else
-      return eina_hash_del_by_data(hash, data);
+      return eina_hash_del_by_data(hash, data) && eina_hash_unlock(hash);
 }
 
 /**
@@ -1335,14 +1462,18 @@ eina_hash_find_by_hash(const Eina_Hash *hash,
    if (!hash)
       return NULL;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
 
    tuple.key = key;
    tuple.key_length = key_length;
    tuple.data = NULL;
 
+   if (!eina_hash_rdlock(hash))
+       return NULL;;
+
    hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head);
+   eina_hash_unlock(hash);
    if (hash_element)
       return hash_element->tuple.data;
 
@@ -1365,9 +1496,12 @@ eina_hash_find(const Eina_Hash *hash, const void *key)
    if (!hash)
       return NULL;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(key,               NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!eina_hash_rdlock(hash))
+       return NULL;
 
    key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
    hash_num = hash->key_hash_cb(key, key_length);
@@ -1398,15 +1532,18 @@ eina_hash_modify_by_hash(Eina_Hash *hash,
    void *old_data = NULL;
    Eina_Hash_Tuple tuple;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(key,  NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
 
    tuple.key = key;
    tuple.key_length = key_length;
    tuple.data = NULL;
 
+   if (!eina_hash_rdlock(hash))
+       return NULL;
+
    hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head);
    if (hash_element)
      {
@@ -1438,11 +1575,14 @@ eina_hash_set(Eina_Hash *hash, const void *key, const void *data)
    int key_length;
    int key_hash;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash,              NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(key,               NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(data,              NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!eina_hash_wrlock(hash))
+       return NULL;
 
    key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
    key_hash = hash->key_hash_cb(key, key_length);
@@ -1458,6 +1598,7 @@ eina_hash_set(Eina_Hash *hash, const void *key, const void *data)
 
         old_data = hash_element->tuple.data;
         hash_element->tuple.data = (void *)data;
+        eina_hash_unlock(hash);
         return old_data;
      }
 
@@ -1467,7 +1608,6 @@ eina_hash_set(Eina_Hash *hash, const void *key, const void *data)
                                key_length,
                                key_hash,
                                data);
-
    return NULL;
 }
 /**
@@ -1485,11 +1625,14 @@ eina_hash_modify(Eina_Hash *hash, const void *key, const void *data)
    int key_length;
    int hash_num;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash,              NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(key,               NULL);
    EINA_SAFETY_ON_NULL_RETURN_VAL(data,              NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!eina_hash_wrlock(hash))
+       return NULL;
 
    key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
    hash_num = hash->key_hash_cb(key, key_length);
@@ -1512,16 +1655,20 @@ eina_hash_move(Eina_Hash *hash, const void *old_key, const void *new_key)
 {
    Eina_Free_Cb hash_free_cb;
    const void *data;
-   Eina_Bool result;
+   Eina_Bool result = EINA_FALSE;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash,              EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(old_key,           EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(new_key,           EINA_FALSE);
+   EINA_MAGIC_CHECK_HASH(hash);
+
+   if (!eina_hash_wrlock(hash))
+       return EINA_FALSE;
 
    data = eina_hash_find(hash, old_key);
-   if (!data) return EINA_FALSE;
+
+   if (!data) goto error;
 
    hash_free_cb = hash->data_free_cb;
    hash->data_free_cb = NULL;
@@ -1531,6 +1678,8 @@ eina_hash_move(Eina_Hash *hash, const void *old_key, const void *new_key)
 
    hash->data_free_cb = hash_free_cb;
 
+error:
+   eina_hash_unlock(hash);
    return result;
 }
 
@@ -1589,7 +1738,17 @@ eina_hash_foreach(const Eina_Hash *hash,
    if (!it)
       return;
 
+   if (!eina_hash_rdlock((Eina_Hash*)hash))
+       return;
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+   ((Eina_Hash*)hash)->stay_locked = EINA_TRUE;
+#endif
+
    eina_iterator_foreach(it, EINA_EACH_CB(_eina_foreach_cb), &foreach);
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+   ((Eina_Hash*)hash)->stay_locked = EINA_FALSE;
+#endif
+   eina_hash_unlock(((Eina_Hash*)hash));
    eina_iterator_free(it);
 }
 
@@ -1617,8 +1776,8 @@ eina_hash_iterator_data_new(const Eina_Hash *hash)
 {
    Eina_Iterator_Hash *it;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
 
         eina_error_set(0);
    it = calloc(1, sizeof (Eina_Iterator_Hash));
@@ -1629,14 +1788,19 @@ eina_hash_iterator_data_new(const Eina_Hash *hash)
      }
 
    it->hash = hash;
-   it->get_content = FUNC_ITERATOR_GET_CONTENT(
-         _eina_hash_iterator_data_get_content);
+   it->get_content = FUNC_ITERATOR_GET_CONTENT(_eina_hash_iterator_data_get_content);
 
    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next);
    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
          _eina_hash_iterator_get_container);
    it->iterator.free = FUNC_ITERATOR_FREE(_eina_hash_iterator_free);
-
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+   if (hash->threadsafe)
+     {
+        it->iterator.lock = (Eina_Iterator_Lock_Callback)_eina_hash_iterator_lock;
+        it->iterator.unlock = (Eina_Iterator_Lock_Callback)_eina_hash_iterator_unlock;
+     }
+#endif
    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
    EINA_MAGIC_SET(it,            EINA_MAGIC_HASH_ITERATOR);
 
@@ -1667,8 +1831,8 @@ eina_hash_iterator_key_new(const Eina_Hash *hash)
 {
    Eina_Iterator_Hash *it;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
 
         eina_error_set(0);
    it = calloc(1, sizeof (Eina_Iterator_Hash));
@@ -1682,6 +1846,13 @@ eina_hash_iterator_key_new(const Eina_Hash *hash)
    it->get_content = FUNC_ITERATOR_GET_CONTENT(
          _eina_hash_iterator_key_get_content);
 
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+   if (hash->threadsafe)
+     {
+        it->iterator.lock = (Eina_Iterator_Lock_Callback)_eina_hash_iterator_lock;
+        it->iterator.unlock = (Eina_Iterator_Lock_Callback)_eina_hash_iterator_unlock;
+     }
+#endif
    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next);
    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
          _eina_hash_iterator_get_container);
@@ -1720,10 +1891,11 @@ eina_hash_iterator_tuple_new(const Eina_Hash *hash)
 {
    Eina_Iterator_Hash *it;
 
-   EINA_MAGIC_CHECK_HASH(hash);
    EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL);
+   EINA_MAGIC_CHECK_HASH(hash);
 
-        eina_error_set(0);
+   
+   eina_error_set(0);
    it = calloc(1, sizeof (Eina_Iterator_Hash));
    if (!it)
      {
@@ -1735,6 +1907,14 @@ eina_hash_iterator_tuple_new(const Eina_Hash *hash)
    it->get_content = FUNC_ITERATOR_GET_CONTENT(
          _eina_hash_iterator_tuple_get_content);
 
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+   if (hash->threadsafe)
+     {
+        it->iterator.lock = (Eina_Iterator_Lock_Callback)_eina_hash_iterator_lock;
+        it->iterator.unlock = (Eina_Iterator_Lock_Callback)_eina_hash_iterator_unlock;
+     }
+#endif
+
    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next);
    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
          _eina_hash_iterator_get_container);
@@ -1802,6 +1982,149 @@ eina_hash_superfast(const char *key, int len)
    return hash;
 }
 
+/* *************************************************
+ * THREADSAFE
+ */
+
+/**
+ * @addtogroup Eina_Hash_Threadsafe_Group Threadsafe Hash
+ * @brief This hash will automatically lock itself upon being accessed, and is safe to use without mutexes in threads.
+ * Threadsafe hash types are identical to regular hash types except that they will always mutex themselves properly upon being accessed
+ * to prevent pointer collision when using the same hash in multiple threads.  They function in exactly the same manner as a regular
+ * hash table, and regular api functions will automatically lock threadsafe hashes.
+ *
+ * All threadsafe api functions have identical arguments to regular api functions.
+ * @{
+ */
+EAPI Eina_Hash *
+eina_hash_threadsafe_new(__UNUSED__ Eina_Key_Length key_length_cb,
+              __UNUSED__ Eina_Key_Cmp key_cmp_cb,
+              __UNUSED__ Eina_Key_Hash key_hash_cb,
+              __UNUSED__ Eina_Free_Cb data_free_cb,
+              __UNUSED__ int buckets_power_size)
+{
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+   /* FIXME: Use mempool. */
+   Eina_Hash *new;
+
+   if (!(new = eina_hash_new(key_length_cb, key_cmp_cb, key_hash_cb, data_free_cb, buckets_power_size)))
+      return NULL;
+   new->threadsafe = EINA_TRUE;
+   new->stay_locked = EINA_FALSE;
+   if (pthread_rwlock_init(&new->lock, NULL))
+      {
+         free(new);
+         goto on_error;
+      }
+
+   return new;
+
+on_error:
+   eina_error_set(EINA_ERROR_OUT_OF_MEMORY);
+#endif
+   return NULL;
+}
+
+/**
+ * @see eina_hash_string_djb2_new
+ */
+EAPI Eina_Hash *
+eina_hash_threadsafe_string_djb2_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_string_key_length),
+                        EINA_KEY_CMP(_eina_string_key_cmp),
+                        EINA_KEY_HASH(eina_hash_djb2),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+}
+
+/**
+ * @brief 
+ * @see eina_hash_string_superfast_new
+ */
+EAPI Eina_Hash *
+eina_hash_threadsafe_string_superfast_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_string_key_length),
+                        EINA_KEY_CMP(_eina_string_key_cmp),
+                        EINA_KEY_HASH(eina_hash_superfast),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+}
+
+/**
+ * @see eina_hash_string_small_new
+ */
+EAPI Eina_Hash *
+eina_hash_threadsafe_string_small_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_string_key_length),
+                        EINA_KEY_CMP(_eina_string_key_cmp),
+                        EINA_KEY_HASH(eina_hash_superfast),
+                        data_free_cb,
+                        EINA_HASH_SMALL_BUCKET_SIZE);
+}
+
 /**
+ * @see eina_hash_int32_new
+ */
+EAPI Eina_Hash *
+eina_hash_threadsafe_int32_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_int32_key_length),
+                        EINA_KEY_CMP(_eina_int32_key_cmp),
+                        EINA_KEY_HASH(eina_hash_int32),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+}
+
+/**
+ * @see eina_hash_int64_new
+ */
+EAPI Eina_Hash *
+eina_hash_threadsafe_int64_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_int64_key_length),
+                        EINA_KEY_CMP(_eina_int64_key_cmp),
+                        EINA_KEY_HASH(eina_hash_int64),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+}
+
+/**
+ * @see eina_hash_pointer_new
+ */
+EAPI Eina_Hash *
+eina_hash_threadsafe_pointer_new(Eina_Free_Cb data_free_cb)
+{
+#ifdef __LP64__
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_int64_key_length),
+                        EINA_KEY_CMP(_eina_int64_key_cmp),
+                        EINA_KEY_HASH(eina_hash_int64),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+#else
+   return eina_hash_new(EINA_KEY_LENGTH(_eina_int32_key_length),
+                        EINA_KEY_CMP(_eina_int32_key_cmp),
+                        EINA_KEY_HASH(eina_hash_int32),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+#endif
+}
+/**
+ * @see eina_hash_stringshared_new
+ */
+EAPI Eina_Hash *
+eina_hash_threadsafe_stringshared_new(Eina_Free_Cb data_free_cb)
+{
+   return eina_hash_new(NULL,
+                        EINA_KEY_CMP(_eina_stringshared_key_cmp),
+                        EINA_KEY_HASH(eina_hash_superfast),
+                        data_free_cb,
+                        EINA_HASH_BUCKET_SIZE);
+}
+
+/**
+ * @}
  * @}
  */
index aeeb655..d2100c6 100644 (file)
@@ -197,10 +197,73 @@ START_TEST(eina_hash_all_int)
 }
 END_TEST
 
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+START_TEST(eina_hash_threadsafe_simple)
+{
+   Eina_Hash *hash = NULL;
+   int *test;
+   int array[] = { 1, 42, 4, 5, 6 };
+
+   /* As mempool is already initialized and it use hash, we should have 2 init. */
+   fail_if(eina_init() != 2);
+
+   hash = eina_hash_threadsafe_string_superfast_new(NULL);
+   fail_if(hash == NULL);
+
+   fail_if(eina_hash_add(hash, "1", &array[0]) != EINA_TRUE);
+   fail_if(eina_hash_add(hash, "42", &array[1]) != EINA_TRUE);
+   fail_if(eina_hash_direct_add(hash, "4", &array[2]) != EINA_TRUE);
+   fail_if(eina_hash_direct_add(hash, "5", &array[3]) != EINA_TRUE);
+   fail_if(eina_hash_add(hash, "", "") != EINA_TRUE);
+
+   test = eina_hash_find(hash, "4");
+   fail_if(!test);
+   fail_if(*test != 4);
+
+   test = eina_hash_find(hash, "42");
+   fail_if(!test);
+   fail_if(*test != 42);
+
+   eina_hash_foreach(hash, eina_foreach_check, NULL);
+
+   test = eina_hash_modify(hash, "5", &array[4]);
+   fail_if(!test);
+   fail_if(*test != 5);
+
+   test = eina_hash_find(hash, "5");
+   fail_if(!test);
+   fail_if(*test != 6);
+
+   fail_if(eina_hash_population(hash) != 5);
+
+   fail_if(eina_hash_find(hash, "120") != NULL);
+
+   fail_if(eina_hash_del(hash, "5", NULL) != EINA_TRUE);
+   fail_if(eina_hash_find(hash, "5") != NULL);
+
+   fail_if(eina_hash_del(hash, NULL, &array[2]) != EINA_TRUE);
+   fail_if(eina_hash_find(hash, "4") != NULL);
+
+   fail_if(eina_hash_del(hash, NULL, &array[2]) != EINA_FALSE);
+
+   fail_if(eina_hash_del(hash, "1", NULL) != EINA_TRUE);
+   fail_if(eina_hash_del(hash, "42", NULL) != EINA_TRUE);
+
+   eina_hash_free(hash);
+
+   /* Same comment as eina_init */
+        fail_if(eina_shutdown() != 1);
+}
+END_TEST
+#endif
+
 void eina_test_hash(TCase *tc)
 {
    tcase_add_test(tc, eina_hash_simple);
    tcase_add_test(tc, eina_hash_extended);
    tcase_add_test(tc, eina_hash_double_item);
    tcase_add_test(tc, eina_hash_all_int);
+#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
+   tcase_add_test(tc, eina_hash_threadsafe_simple);
+#endif
 }