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_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);

View 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);
}
/**
* @}
* @}
*/

View 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
}