add threadsafety for hashes

SVN revision: 51106
This commit is contained in:
Mike Blumenkrantz 2010-08-14 03:05:35 +00:00
parent f973f8c72a
commit 689a24559c
3 changed files with 439 additions and 39 deletions

View File

@ -23,6 +23,10 @@
#include "eina_types.h" #include "eina_types.h"
#include "eina_iterator.h" #include "eina_iterator.h"
#ifdef EINA_RWLOCKS_ENABLED
# include <pthread.h>
# include <errno.h>
#endif
/** /**
* @addtogroup Eina_Data_Types_Group Data Types * @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_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_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_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_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); 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_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; 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); 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); EAPI void eina_hash_foreach(const Eina_Hash *hash, Eina_Hash_Foreach cb, const void *fdata) EINA_ARG_NONNULL(1, 2);

View File

@ -86,8 +86,9 @@ struct _Eina_Hash
int population; int population;
#ifdef EINA_RWLOCKS_ENABLED #ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
pthread_rwlock_t lock; pthread_rwlock_t lock;
Eina_Bool stay_locked:1; /* useful for iterator functions which don't want to lose lock */
Eina_Bool threadsafe:1; Eina_Bool threadsafe:1;
#endif #endif
@ -107,9 +108,11 @@ struct _Eina_Hash_Element
EINA_RBTREE; EINA_RBTREE;
Eina_Hash_Tuple tuple; Eina_Hash_Tuple tuple;
Eina_Bool begin : 1; 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; pthread_rwlock_t lock;
int threadcount; int threadcount;
# endif
#endif #endif
}; };
@ -158,6 +161,71 @@ struct _Eina_Hash_Each
+ (uint32_t)(((const uint8_t *)(d))[0])) + (uint32_t)(((const uint8_t *)(d))[0]))
#endif #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 static inline int
_eina_hash_hash_rbtree_cmp_hash(const Eina_Hash_Head *hash_head, _eina_hash_hash_rbtree_cmp_hash(const Eina_Hash_Head *hash_head,
const int *hash, const int *hash,
@ -224,10 +292,13 @@ eina_hash_add_alloc_by_hash(Eina_Hash *hash,
Eina_Error error = 0; Eina_Error error = 0;
int hash_num; int hash_num;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(data, 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; error = EINA_ERROR_OUT_OF_MEMORY;
/* Apply eina mask to hash. */ /* Apply eina mask to hash. */
@ -250,7 +321,7 @@ eina_hash_add_alloc_by_hash(Eina_Hash *hash,
if (!hash_head) 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); hash_head = malloc(sizeof(Eina_Hash_Head) + sizeof(Eina_Hash_Element) + alloc_length);
if (!hash_head) if (!hash_head)
goto on_error; goto on_error;
@ -270,7 +341,7 @@ eina_hash_add_alloc_by_hash(Eina_Hash *hash,
if (!new_hash_element) 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). (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); 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), _eina_hash_key_rbtree_cmp_node),
(const void *)hash->key_cmp_cb); (const void *)hash->key_cmp_cb);
hash->population++; hash->population++;
eina_hash_unlock(hash);
return EINA_TRUE; return EINA_TRUE;
on_error: on_error:
eina_error_set(error); eina_error_set(error);
eina_hash_unlock(hash);
return EINA_FALSE; return EINA_FALSE;
} }
@ -459,9 +532,9 @@ _eina_hash_del_by_key_hash(Eina_Hash *hash,
Eina_Hash_Head *hash_head; Eina_Hash_Head *hash_head;
Eina_Hash_Tuple tuple; Eina_Hash_Tuple tuple;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
EINA_MAGIC_CHECK_HASH(hash);
if (!hash->buckets) if (!hash->buckets)
return EINA_FALSE; return EINA_FALSE;
@ -698,6 +771,22 @@ _eina_hash_iterator_free(Eina_Iterator_Hash *it)
free(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 * @endcond
*/ */
@ -761,6 +850,10 @@ eina_hash_new(Eina_Key_Length key_length_cb,
new->data_free_cb = data_free_cb; new->data_free_cb = data_free_cb;
new->buckets = NULL; new->buckets = NULL;
new->population = 0; 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->size = 1 << buckets_power_size;
new->mask = new->size - 1; new->mask = new->size - 1;
@ -969,14 +1062,18 @@ eina_hash_free(Eina_Hash *hash)
EINA_MAGIC_CHECK_HASH(hash); EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN(hash); EINA_SAFETY_ON_NULL_RETURN(hash);
if (!eina_hash_wrlock(hash))
return;
if (hash->buckets) if (hash->buckets)
{ {
for (i = 0; i < hash->size; i++) for (i = 0; i < hash->size; i++)
eina_rbtree_delete(hash->buckets[i], eina_rbtree_delete(hash->buckets[i], EINA_RBTREE_FREE_CB(_eina_hash_head_free), hash);
EINA_RBTREE_FREE_CB(_eina_hash_head_free), hash);
free(hash->buckets); free(hash->buckets);
} }
#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
if (hash->threadsafe)
pthread_rwlock_destroy(&hash->lock);
#endif
free(hash); free(hash);
} }
@ -997,6 +1094,8 @@ eina_hash_free_buckets(Eina_Hash *hash)
EINA_MAGIC_CHECK_HASH(hash); EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN(hash); EINA_SAFETY_ON_NULL_RETURN(hash);
if (!eina_hash_wrlock(hash))
return;
if (hash->buckets) if (hash->buckets)
{ {
for (i = 0; i < hash->size; i++) for (i = 0; i < hash->size; i++)
@ -1006,6 +1105,7 @@ eina_hash_free_buckets(Eina_Hash *hash)
hash->buckets = NULL; hash->buckets = NULL;
hash->population = 0; 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(key, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(data, 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_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
key_hash = hash->key_hash_cb(key, key_length); 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_length;
int key_hash; int key_hash;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); 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(hash->key_hash_cb, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(key, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(data, 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_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
key_hash = hash->key_hash_cb(key, key_length); 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(hash, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, 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(hash, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, 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; Eina_Hash_Head *hash_head;
int key_hash; int key_hash;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(data, 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); hash_element = _eina_hash_find_by_data(hash, data, &key_hash, &hash_head);
if (!hash_element) if (!hash_element)
return EINA_FALSE; goto error;
if (hash_element->tuple.data != data) 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, int key_hash,
const void *data) const void *data)
{ {
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
EINA_MAGIC_CHECK_HASH(hash);
if (!eina_hash_wrlock(hash))
return EINA_FALSE;
if (key) 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 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 EAPI Eina_Bool
eina_hash_del(Eina_Hash *hash, const void *key, const void *data) 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_SAFETY_ON_NULL_RETURN_VAL(hash, EINA_FALSE);
EINA_MAGIC_CHECK_HASH(hash);
if (!eina_hash_wrlock(hash))
return EINA_FALSE;
if (key) 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 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) if (!hash)
return NULL; return NULL;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
EINA_MAGIC_CHECK_HASH(hash);
tuple.key = key; tuple.key = key;
tuple.key_length = key_length; tuple.key_length = key_length;
tuple.data = NULL; tuple.data = NULL;
if (!eina_hash_rdlock(hash))
return NULL;;
hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head); hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head);
eina_hash_unlock(hash);
if (hash_element) if (hash_element)
return hash_element->tuple.data; return hash_element->tuple.data;
@ -1365,9 +1496,12 @@ eina_hash_find(const Eina_Hash *hash, const void *key)
if (!hash) if (!hash)
return NULL; return NULL;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, 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(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; key_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
hash_num = hash->key_hash_cb(key, key_length); 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; void *old_data = NULL;
Eina_Hash_Tuple tuple; Eina_Hash_Tuple tuple;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(data, NULL);
EINA_MAGIC_CHECK_HASH(hash);
tuple.key = key; tuple.key = key;
tuple.key_length = key_length; tuple.key_length = key_length;
tuple.data = NULL; tuple.data = NULL;
if (!eina_hash_rdlock(hash))
return NULL;
hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head); hash_element = _eina_hash_find_by_hash(hash, &tuple, key_hash, &hash_head);
if (hash_element) if (hash_element)
{ {
@ -1438,11 +1575,14 @@ eina_hash_set(Eina_Hash *hash, const void *key, const void *data)
int key_length; int key_length;
int key_hash; int key_hash;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); 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(hash->key_hash_cb, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(data, 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_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
key_hash = hash->key_hash_cb(key, key_length); 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; old_data = hash_element->tuple.data;
hash_element->tuple.data = (void *)data; hash_element->tuple.data = (void *)data;
eina_hash_unlock(hash);
return old_data; return old_data;
} }
@ -1467,7 +1608,6 @@ eina_hash_set(Eina_Hash *hash, const void *key, const void *data)
key_length, key_length,
key_hash, key_hash,
data); data);
return NULL; return NULL;
} }
/** /**
@ -1485,11 +1625,14 @@ eina_hash_modify(Eina_Hash *hash, const void *key, const void *data)
int key_length; int key_length;
int hash_num; int hash_num;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); 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(hash->key_hash_cb, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(key, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(data, 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_length = hash->key_length_cb ? hash->key_length_cb(key) : 0;
hash_num = hash->key_hash_cb(key, key_length); 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; Eina_Free_Cb hash_free_cb;
const void *data; 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, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash->key_hash_cb, 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(old_key, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(new_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); data = eina_hash_find(hash, old_key);
if (!data) return EINA_FALSE;
if (!data) goto error;
hash_free_cb = hash->data_free_cb; hash_free_cb = hash->data_free_cb;
hash->data_free_cb = NULL; 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; hash->data_free_cb = hash_free_cb;
error:
eina_hash_unlock(hash);
return result; return result;
} }
@ -1589,7 +1738,17 @@ eina_hash_foreach(const Eina_Hash *hash,
if (!it) if (!it)
return; 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); 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); eina_iterator_free(it);
} }
@ -1617,8 +1776,8 @@ eina_hash_iterator_data_new(const Eina_Hash *hash)
{ {
Eina_Iterator_Hash *it; Eina_Iterator_Hash *it;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); 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)); it = calloc(1, sizeof (Eina_Iterator_Hash));
@ -1629,14 +1788,19 @@ eina_hash_iterator_data_new(const Eina_Hash *hash)
} }
it->hash = hash; it->hash = hash;
it->get_content = FUNC_ITERATOR_GET_CONTENT( it->get_content = FUNC_ITERATOR_GET_CONTENT(_eina_hash_iterator_data_get_content);
_eina_hash_iterator_data_get_content);
it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next); it->iterator.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next);
it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
_eina_hash_iterator_get_container); _eina_hash_iterator_get_container);
it->iterator.free = FUNC_ITERATOR_FREE(_eina_hash_iterator_free); 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->iterator, EINA_MAGIC_ITERATOR);
EINA_MAGIC_SET(it, EINA_MAGIC_HASH_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_Iterator_Hash *it;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); 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)); 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( it->get_content = FUNC_ITERATOR_GET_CONTENT(
_eina_hash_iterator_key_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.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next);
it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
_eina_hash_iterator_get_container); _eina_hash_iterator_get_container);
@ -1720,8 +1891,9 @@ eina_hash_iterator_tuple_new(const Eina_Hash *hash)
{ {
Eina_Iterator_Hash *it; Eina_Iterator_Hash *it;
EINA_MAGIC_CHECK_HASH(hash);
EINA_SAFETY_ON_NULL_RETURN_VAL(hash, NULL); 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)); it = calloc(1, sizeof (Eina_Iterator_Hash));
@ -1735,6 +1907,14 @@ eina_hash_iterator_tuple_new(const Eina_Hash *hash)
it->get_content = FUNC_ITERATOR_GET_CONTENT( it->get_content = FUNC_ITERATOR_GET_CONTENT(
_eina_hash_iterator_tuple_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.next = FUNC_ITERATOR_NEXT(_eina_hash_iterator_next);
it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER( it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(
_eina_hash_iterator_get_container); _eina_hash_iterator_get_container);
@ -1802,6 +1982,149 @@ eina_hash_superfast(const char *key, int len)
return hash; 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);
}
/** /**
* @}
* @} * @}
*/ */

View File

@ -197,10 +197,73 @@ START_TEST(eina_hash_all_int)
} }
END_TEST 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) void eina_test_hash(TCase *tc)
{ {
tcase_add_test(tc, eina_hash_simple); tcase_add_test(tc, eina_hash_simple);
tcase_add_test(tc, eina_hash_extended); tcase_add_test(tc, eina_hash_extended);
tcase_add_test(tc, eina_hash_double_item); tcase_add_test(tc, eina_hash_double_item);
tcase_add_test(tc, eina_hash_all_int); tcase_add_test(tc, eina_hash_all_int);
#ifdef EFL_HAVE_POSIX_THREADS_RWLOCK
tcase_add_test(tc, eina_hash_threadsafe_simple);
#endif
} }