Trivial changes for thread sanitizer
Thread sanitizer warns about reads and writes of variables that are meant to be read without holding a lock: * Global "keep_going" is now a ddsrt_atomic_uint32_t * Thread "vtime" is now a ddsrt_atomic_uint32_t Previously the code relied on the assumption that a 32-bit int would be treated as atomic, now that is all wrapped in ddsrt_atomic_{ld,st}32. These being inline functions doing exactly the same thing, there is no functional change, but it does allow annotating the loads and stores for via function attributes on the ddsrt_atomic_{ld,st}X. The concurrent hashtable implementation is replaced by a locked version of the non-concurrent implementation if thread sanitizer is used. This changes eliminates the scores of problems signalled by thread sanitizer in the GUID-to-entity translation and the key-to-instance id lookups. Other than that, this replaces a flag used in a waitset test case to be a ddsrt_atomic_uint32_t. Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
parent
0356af470d
commit
c6c5a872eb
17 changed files with 404 additions and 267 deletions
|
@ -13,6 +13,7 @@
|
|||
#define DDSRT_ATOMICS_GCC_H
|
||||
|
||||
#include "dds/ddsrt/misc.h"
|
||||
#include "dds/ddsrt/attributes.h"
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
|
@ -25,19 +26,51 @@ extern "C" {
|
|||
|
||||
/* LD, ST */
|
||||
|
||||
inline uint32_t ddsrt_atomic_ld32(const volatile ddsrt_atomic_uint32_t *x) { return x->v; }
|
||||
ddsrt_attribute_no_sanitize (("thread"))
|
||||
inline uint32_t ddsrt_atomic_ld32(const volatile ddsrt_atomic_uint32_t *x)
|
||||
{
|
||||
return x->v;
|
||||
}
|
||||
#if DDSRT_HAVE_ATOMIC64
|
||||
inline uint64_t ddsrt_atomic_ld64(const volatile ddsrt_atomic_uint64_t *x) { return x->v; }
|
||||
ddsrt_attribute_no_sanitize (("thread"))
|
||||
inline uint64_t ddsrt_atomic_ld64(const volatile ddsrt_atomic_uint64_t *x)
|
||||
{
|
||||
return x->v;
|
||||
}
|
||||
#endif
|
||||
inline uintptr_t ddsrt_atomic_ldptr(const volatile ddsrt_atomic_uintptr_t *x) { return x->v; }
|
||||
inline void *ddsrt_atomic_ldvoidp(const volatile ddsrt_atomic_voidp_t *x) { return (void *) ddsrt_atomic_ldptr(x); }
|
||||
ddsrt_attribute_no_sanitize (("thread"))
|
||||
inline uintptr_t ddsrt_atomic_ldptr(const volatile ddsrt_atomic_uintptr_t *x)
|
||||
{
|
||||
return x->v;
|
||||
}
|
||||
ddsrt_attribute_no_sanitize (("thread"))
|
||||
inline void *ddsrt_atomic_ldvoidp(const volatile ddsrt_atomic_voidp_t *x)
|
||||
{
|
||||
return (void *) ddsrt_atomic_ldptr(x);
|
||||
}
|
||||
|
||||
inline void ddsrt_atomic_st32(volatile ddsrt_atomic_uint32_t *x, uint32_t v) { x->v = v; }
|
||||
ddsrt_attribute_no_sanitize (("thread"))
|
||||
inline void ddsrt_atomic_st32(volatile ddsrt_atomic_uint32_t *x, uint32_t v)
|
||||
{
|
||||
x->v = v;
|
||||
}
|
||||
#if DDSRT_HAVE_ATOMIC64
|
||||
inline void ddsrt_atomic_st64(volatile ddsrt_atomic_uint64_t *x, uint64_t v) { x->v = v; }
|
||||
ddsrt_attribute_no_sanitize (("thread"))
|
||||
inline void ddsrt_atomic_st64(volatile ddsrt_atomic_uint64_t *x, uint64_t v)
|
||||
{
|
||||
x->v = v;
|
||||
}
|
||||
#endif
|
||||
inline void ddsrt_atomic_stptr(volatile ddsrt_atomic_uintptr_t *x, uintptr_t v) { x->v = v; }
|
||||
inline void ddsrt_atomic_stvoidp(volatile ddsrt_atomic_voidp_t *x, void *v) { ddsrt_atomic_stptr(x, (uintptr_t)v); }
|
||||
ddsrt_attribute_no_sanitize (("thread"))
|
||||
inline void ddsrt_atomic_stptr(volatile ddsrt_atomic_uintptr_t *x, uintptr_t v)
|
||||
{
|
||||
x->v = v;
|
||||
}
|
||||
ddsrt_attribute_no_sanitize (("thread"))
|
||||
inline void ddsrt_atomic_stvoidp(volatile ddsrt_atomic_voidp_t *x, void *v)
|
||||
{
|
||||
ddsrt_atomic_stptr(x, (uintptr_t)v);
|
||||
}
|
||||
|
||||
/* INC */
|
||||
|
||||
|
|
|
@ -111,4 +111,16 @@
|
|||
# define ddsrt_attribute_packed
|
||||
#endif
|
||||
|
||||
#if ddsrt_has_attribute(no_sanitize)
|
||||
# define ddsrt_attribute_no_sanitize(params) __attribute__ ((__no_sanitize__ params))
|
||||
#else
|
||||
# define ddsrt_attribute_no_sanitize(params)
|
||||
#endif
|
||||
|
||||
#if defined(__has_feature)
|
||||
# define ddsrt_has_feature_thread_sanitizer __has_feature(thread_sanitizer)
|
||||
#else
|
||||
# define ddsrt_has_feature_thread_sanitizer 0
|
||||
#endif
|
||||
|
||||
#endif /* DDSRT_ATTRIBUTES_H */
|
||||
|
|
|
@ -20,15 +20,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Concurrent version */
|
||||
struct ddsrt_chh;
|
||||
struct ddsrt_chh_bucket;
|
||||
struct ddsrt_chh_iter {
|
||||
struct ddsrt_chh_bucket *bs;
|
||||
uint32_t size;
|
||||
uint32_t cursor;
|
||||
};
|
||||
|
||||
/*
|
||||
* The hopscotch hash table is dependent on a proper functioning hash.
|
||||
* If the hash function generates a lot of hash collisions, then it will
|
||||
|
@ -54,15 +45,6 @@ typedef int (*ddsrt_hh_equals_fn) (const void *, const void *);
|
|||
*/
|
||||
typedef void (*ddsrt_hh_buckets_gc_fn) (void *);
|
||||
|
||||
DDS_EXPORT struct ddsrt_chh *ddsrt_chh_new (uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals, ddsrt_hh_buckets_gc_fn gc_buckets);
|
||||
DDS_EXPORT void ddsrt_chh_free (struct ddsrt_chh * __restrict hh);
|
||||
DDS_EXPORT void *ddsrt_chh_lookup (struct ddsrt_chh * __restrict rt, const void * __restrict template);
|
||||
DDS_EXPORT int ddsrt_chh_add (struct ddsrt_chh * __restrict rt, const void * __restrict data);
|
||||
DDS_EXPORT int ddsrt_chh_remove (struct ddsrt_chh * __restrict rt, const void * __restrict template);
|
||||
DDS_EXPORT void ddsrt_chh_enum_unsafe (struct ddsrt_chh * __restrict rt, void (*f) (void *a, void *f_arg), void *f_arg); /* may delete a */
|
||||
void *ddsrt_chh_iter_first (struct ddsrt_chh * __restrict rt, struct ddsrt_chh_iter *it);
|
||||
void *ddsrt_chh_iter_next (struct ddsrt_chh_iter *it);
|
||||
|
||||
/* Sequential version */
|
||||
struct ddsrt_hh;
|
||||
|
||||
|
@ -80,6 +62,31 @@ DDS_EXPORT void ddsrt_hh_enum (struct ddsrt_hh * __restrict rt, void (*f) (void
|
|||
DDS_EXPORT void *ddsrt_hh_iter_first (struct ddsrt_hh * __restrict rt, struct ddsrt_hh_iter * __restrict iter); /* may delete nodes */
|
||||
DDS_EXPORT void *ddsrt_hh_iter_next (struct ddsrt_hh_iter * __restrict iter);
|
||||
|
||||
/* Concurrent version */
|
||||
struct ddsrt_chh;
|
||||
struct ddsrt_chh_bucket;
|
||||
|
||||
#if ! ddsrt_has_feature_thread_sanitizer
|
||||
struct ddsrt_chh_iter {
|
||||
struct ddsrt_chh_bucket *bs;
|
||||
uint32_t size;
|
||||
uint32_t cursor;
|
||||
};
|
||||
#else
|
||||
struct ddsrt_chh_iter {
|
||||
struct ddsrt_chh *chh;
|
||||
struct ddsrt_hh_iter it;
|
||||
};
|
||||
#endif
|
||||
|
||||
DDS_EXPORT struct ddsrt_chh *ddsrt_chh_new (uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals, ddsrt_hh_buckets_gc_fn gc_buckets);
|
||||
DDS_EXPORT void ddsrt_chh_free (struct ddsrt_chh * __restrict hh);
|
||||
DDS_EXPORT void *ddsrt_chh_lookup (struct ddsrt_chh * __restrict rt, const void * __restrict template);
|
||||
DDS_EXPORT int ddsrt_chh_add (struct ddsrt_chh * __restrict rt, const void * __restrict data);
|
||||
DDS_EXPORT int ddsrt_chh_remove (struct ddsrt_chh * __restrict rt, const void * __restrict template);
|
||||
DDS_EXPORT void ddsrt_chh_enum_unsafe (struct ddsrt_chh * __restrict rt, void (*f) (void *a, void *f_arg), void *f_arg); /* may delete a */
|
||||
DDS_EXPORT void *ddsrt_chh_iter_first (struct ddsrt_chh * __restrict rt, struct ddsrt_chh_iter *it);
|
||||
DDS_EXPORT void *ddsrt_chh_iter_next (struct ddsrt_chh_iter *it);
|
||||
/* Sequential version, embedded data */
|
||||
struct ddsrt_ehh;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dds/ddsrt/attributes.h"
|
||||
#include "dds/ddsrt/atomics.h"
|
||||
#include "dds/ddsrt/heap.h"
|
||||
#include "dds/ddsrt/sync.h"
|
||||
|
@ -22,8 +23,229 @@
|
|||
|
||||
#define NOT_A_BUCKET (~(uint32_t)0)
|
||||
|
||||
/************* SEQUENTIAL VERSION ***************/
|
||||
|
||||
struct ddsrt_hh_bucket {
|
||||
uint32_t hopinfo;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct ddsrt_hh {
|
||||
uint32_t size; /* power of 2 */
|
||||
struct ddsrt_hh_bucket *buckets;
|
||||
ddsrt_hh_hash_fn hash;
|
||||
ddsrt_hh_equals_fn equals;
|
||||
};
|
||||
|
||||
static void ddsrt_hh_init (struct ddsrt_hh *rt, uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals)
|
||||
{
|
||||
uint32_t size = HH_HOP_RANGE;
|
||||
uint32_t i;
|
||||
while (size < init_size) {
|
||||
size *= 2;
|
||||
}
|
||||
rt->hash = hash;
|
||||
rt->equals = equals;
|
||||
rt->size = size;
|
||||
rt->buckets = ddsrt_malloc (size * sizeof (*rt->buckets));
|
||||
for (i = 0; i < size; i++) {
|
||||
rt->buckets[i].hopinfo = 0;
|
||||
rt->buckets[i].data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void ddsrt_hh_fini (struct ddsrt_hh *rt)
|
||||
{
|
||||
ddsrt_free (rt->buckets);
|
||||
}
|
||||
|
||||
struct ddsrt_hh *ddsrt_hh_new (uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals)
|
||||
{
|
||||
struct ddsrt_hh *hh = ddsrt_malloc (sizeof (*hh));
|
||||
ddsrt_hh_init (hh, init_size, hash, equals);
|
||||
return hh;
|
||||
}
|
||||
|
||||
void ddsrt_hh_free (struct ddsrt_hh * __restrict hh)
|
||||
{
|
||||
ddsrt_hh_fini (hh);
|
||||
ddsrt_free (hh);
|
||||
}
|
||||
|
||||
static void *ddsrt_hh_lookup_internal (const struct ddsrt_hh *rt, const uint32_t bucket, const void *template)
|
||||
{
|
||||
const uint32_t idxmask = rt->size - 1;
|
||||
uint32_t hopinfo = rt->buckets[bucket].hopinfo;
|
||||
uint32_t idx;
|
||||
for (idx = 0; hopinfo != 0; hopinfo >>= 1, idx++) {
|
||||
const uint32_t bidx = (bucket + idx) & idxmask;
|
||||
void *data = rt->buckets[bidx].data;
|
||||
if (data && rt->equals (data, template))
|
||||
return data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ddsrt_hh_lookup (const struct ddsrt_hh * __restrict rt, const void * __restrict template)
|
||||
{
|
||||
const uint32_t hash = rt->hash (template);
|
||||
const uint32_t idxmask = rt->size - 1;
|
||||
const uint32_t bucket = hash & idxmask;
|
||||
return ddsrt_hh_lookup_internal (rt, bucket, template);
|
||||
}
|
||||
|
||||
static uint32_t ddsrt_hh_find_closer_free_bucket (struct ddsrt_hh *rt, uint32_t free_bucket, uint32_t *free_distance)
|
||||
{
|
||||
const uint32_t idxmask = rt->size - 1;
|
||||
uint32_t move_bucket, free_dist;
|
||||
move_bucket = (free_bucket - (HH_HOP_RANGE - 1)) & idxmask;
|
||||
for (free_dist = HH_HOP_RANGE - 1; free_dist > 0; free_dist--) {
|
||||
uint32_t move_free_distance = NOT_A_BUCKET;
|
||||
uint32_t mask = 1;
|
||||
uint32_t i;
|
||||
for (i = 0; i < free_dist; i++, mask <<= 1) {
|
||||
if (mask & rt->buckets[move_bucket].hopinfo) {
|
||||
move_free_distance = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (move_free_distance != NOT_A_BUCKET) {
|
||||
uint32_t new_free_bucket = (move_bucket + move_free_distance) & idxmask;
|
||||
rt->buckets[move_bucket].hopinfo |= 1u << free_dist;
|
||||
rt->buckets[free_bucket].data = rt->buckets[new_free_bucket].data;
|
||||
rt->buckets[new_free_bucket].data = NULL;
|
||||
rt->buckets[move_bucket].hopinfo &= ~(1u << move_free_distance);
|
||||
*free_distance -= free_dist - move_free_distance;
|
||||
return new_free_bucket;
|
||||
}
|
||||
move_bucket = (move_bucket + 1) & idxmask;
|
||||
}
|
||||
return NOT_A_BUCKET;
|
||||
}
|
||||
|
||||
static void ddsrt_hh_resize (struct ddsrt_hh *rt)
|
||||
{
|
||||
struct ddsrt_hh_bucket *bs1;
|
||||
uint32_t i, idxmask0, idxmask1;
|
||||
|
||||
bs1 = ddsrt_malloc (2 * rt->size * sizeof (*rt->buckets));
|
||||
|
||||
for (i = 0; i < 2 * rt->size; i++) {
|
||||
bs1[i].hopinfo = 0;
|
||||
bs1[i].data = NULL;
|
||||
}
|
||||
idxmask0 = rt->size - 1;
|
||||
idxmask1 = 2 * rt->size - 1;
|
||||
for (i = 0; i < rt->size; i++) {
|
||||
void *data = rt->buckets[i].data;
|
||||
if (data) {
|
||||
const uint32_t hash = rt->hash (data);
|
||||
const uint32_t old_start_bucket = hash & idxmask0;
|
||||
const uint32_t new_start_bucket = hash & idxmask1;
|
||||
const uint32_t dist = (i >= old_start_bucket) ? (i - old_start_bucket) : (rt->size + i - old_start_bucket);
|
||||
const uint32_t newb = (new_start_bucket + dist) & idxmask1;
|
||||
assert (dist < HH_HOP_RANGE);
|
||||
bs1[new_start_bucket].hopinfo |= 1u << dist;
|
||||
bs1[newb].data = data;
|
||||
}
|
||||
}
|
||||
|
||||
ddsrt_free (rt->buckets);
|
||||
rt->size *= 2;
|
||||
rt->buckets = bs1;
|
||||
}
|
||||
|
||||
int ddsrt_hh_add (struct ddsrt_hh * __restrict rt, const void * __restrict data)
|
||||
{
|
||||
const uint32_t hash = rt->hash (data);
|
||||
const uint32_t idxmask = rt->size - 1;
|
||||
const uint32_t start_bucket = hash & idxmask;
|
||||
uint32_t free_distance, free_bucket;
|
||||
|
||||
if (ddsrt_hh_lookup_internal (rt, start_bucket, data)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
free_bucket = start_bucket;
|
||||
for (free_distance = 0; free_distance < HH_ADD_RANGE; free_distance++) {
|
||||
if (rt->buckets[free_bucket].data == NULL)
|
||||
break;
|
||||
free_bucket = (free_bucket + 1) & idxmask;
|
||||
}
|
||||
if (free_distance < HH_ADD_RANGE) {
|
||||
do {
|
||||
if (free_distance < HH_HOP_RANGE) {
|
||||
assert ((uint32_t) free_bucket == ((start_bucket + free_distance) & idxmask));
|
||||
rt->buckets[start_bucket].hopinfo |= 1u << free_distance;
|
||||
rt->buckets[free_bucket].data = (void *) data;
|
||||
return 1;
|
||||
}
|
||||
free_bucket = ddsrt_hh_find_closer_free_bucket (rt, free_bucket, &free_distance);
|
||||
assert (free_bucket == NOT_A_BUCKET || free_bucket <= idxmask);
|
||||
} while (free_bucket != NOT_A_BUCKET);
|
||||
}
|
||||
|
||||
ddsrt_hh_resize (rt);
|
||||
return ddsrt_hh_add (rt, data);
|
||||
}
|
||||
|
||||
int ddsrt_hh_remove (struct ddsrt_hh * __restrict rt, const void * __restrict template)
|
||||
{
|
||||
const uint32_t hash = rt->hash (template);
|
||||
const uint32_t idxmask = rt->size - 1;
|
||||
const uint32_t bucket = hash & idxmask;
|
||||
uint32_t hopinfo;
|
||||
uint32_t idx;
|
||||
hopinfo = rt->buckets[bucket].hopinfo;
|
||||
for (idx = 0; hopinfo != 0; hopinfo >>= 1, idx++) {
|
||||
if (hopinfo & 1) {
|
||||
const uint32_t bidx = (bucket + idx) & idxmask;
|
||||
void *data = rt->buckets[bidx].data;
|
||||
if (data && rt->equals (data, template)) {
|
||||
rt->buckets[bidx].data = NULL;
|
||||
rt->buckets[bucket].hopinfo &= ~(1u << idx);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ddsrt_hh_enum (struct ddsrt_hh * __restrict rt, void (*f) (void *a, void *f_arg), void *f_arg)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < rt->size; i++) {
|
||||
void *data = rt->buckets[i].data;
|
||||
if (data) {
|
||||
f (data, f_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *ddsrt_hh_iter_first (struct ddsrt_hh * __restrict rt, struct ddsrt_hh_iter * __restrict iter)
|
||||
{
|
||||
iter->hh = rt;
|
||||
iter->cursor = 0;
|
||||
return ddsrt_hh_iter_next (iter);
|
||||
}
|
||||
|
||||
void *ddsrt_hh_iter_next (struct ddsrt_hh_iter * __restrict iter)
|
||||
{
|
||||
struct ddsrt_hh *rt = iter->hh;
|
||||
while (iter->cursor < rt->size) {
|
||||
void *data = rt->buckets[iter->cursor].data;
|
||||
iter->cursor++;
|
||||
if (data) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/********** CONCURRENT VERSION ************/
|
||||
|
||||
#if ! ddsrt_has_feature_thread_sanitizer
|
||||
|
||||
#define N_BACKING_LOCKS 32
|
||||
#define N_RESIZE_LOCKS 8
|
||||
|
||||
|
@ -228,7 +450,7 @@ static void *ddsrt_chh_lookup_internal (struct ddsrt_chh_bucket_array const * co
|
|||
|
||||
#define ddsrt_atomic_rmw32_nonatomic(var_, tmp_, expr_) do { \
|
||||
ddsrt_atomic_uint32_t *var__ = (var_); \
|
||||
uint32_t tmp_ = ddsrt_atomic_ld32 (var__); \
|
||||
uint32_t tmp_ = ddsrt_atomic_ld32 (var__); \
|
||||
ddsrt_atomic_st32 (var__, (expr_)); \
|
||||
} while (0)
|
||||
|
||||
|
@ -467,224 +689,78 @@ void *ddsrt_chh_iter_first (struct ddsrt_chh * __restrict rt, struct ddsrt_chh_i
|
|||
return ddsrt_chh_iter_next (it);
|
||||
}
|
||||
|
||||
/************* SEQUENTIAL VERSION ***************/
|
||||
#else
|
||||
|
||||
struct ddsrt_hh_bucket {
|
||||
uint32_t hopinfo;
|
||||
void *data;
|
||||
struct ddsrt_chh {
|
||||
ddsrt_mutex_t lock;
|
||||
struct ddsrt_hh rt;
|
||||
};
|
||||
|
||||
struct ddsrt_hh {
|
||||
uint32_t size; /* power of 2 */
|
||||
struct ddsrt_hh_bucket *buckets;
|
||||
ddsrt_hh_hash_fn hash;
|
||||
ddsrt_hh_equals_fn equals;
|
||||
};
|
||||
|
||||
static void ddsrt_hh_init (struct ddsrt_hh *rt, uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals)
|
||||
struct ddsrt_chh *ddsrt_chh_new (uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals, ddsrt_hh_buckets_gc_fn gc)
|
||||
{
|
||||
uint32_t size = HH_HOP_RANGE;
|
||||
uint32_t i;
|
||||
while (size < init_size) {
|
||||
size *= 2;
|
||||
}
|
||||
rt->hash = hash;
|
||||
rt->equals = equals;
|
||||
rt->size = size;
|
||||
rt->buckets = ddsrt_malloc (size * sizeof (*rt->buckets));
|
||||
for (i = 0; i < size; i++) {
|
||||
rt->buckets[i].hopinfo = 0;
|
||||
rt->buckets[i].data = NULL;
|
||||
}
|
||||
struct ddsrt_chh *hh = ddsrt_malloc (sizeof (*hh));
|
||||
(void) gc;
|
||||
ddsrt_mutex_init (&hh->lock);
|
||||
ddsrt_hh_init (&hh->rt, init_size, hash, equals);
|
||||
return hh;
|
||||
}
|
||||
|
||||
static void ddsrt_hh_fini (struct ddsrt_hh *rt)
|
||||
void ddsrt_chh_free (struct ddsrt_chh * __restrict hh)
|
||||
{
|
||||
ddsrt_free (rt->buckets);
|
||||
ddsrt_hh_fini (&hh->rt);
|
||||
ddsrt_mutex_destroy (&hh->lock);
|
||||
ddsrt_free (hh);
|
||||
}
|
||||
|
||||
struct ddsrt_hh *ddsrt_hh_new (uint32_t init_size, ddsrt_hh_hash_fn hash, ddsrt_hh_equals_fn equals)
|
||||
void *ddsrt_chh_lookup (struct ddsrt_chh * __restrict hh, const void * __restrict template)
|
||||
{
|
||||
struct ddsrt_hh *hh = ddsrt_malloc (sizeof (*hh));
|
||||
ddsrt_hh_init (hh, init_size, hash, equals);
|
||||
return hh;
|
||||
ddsrt_mutex_lock (&hh->lock);
|
||||
void *x = ddsrt_hh_lookup (&hh->rt, template);
|
||||
ddsrt_mutex_unlock (&hh->lock);
|
||||
return x;
|
||||
}
|
||||
|
||||
void ddsrt_hh_free (struct ddsrt_hh * __restrict hh)
|
||||
int ddsrt_chh_add (struct ddsrt_chh * __restrict hh, const void * __restrict data)
|
||||
{
|
||||
ddsrt_hh_fini (hh);
|
||||
ddsrt_free (hh);
|
||||
ddsrt_mutex_lock (&hh->lock);
|
||||
int x = ddsrt_hh_add (&hh->rt, data);
|
||||
ddsrt_mutex_unlock (&hh->lock);
|
||||
return x;
|
||||
}
|
||||
|
||||
static void *ddsrt_hh_lookup_internal (const struct ddsrt_hh *rt, const uint32_t bucket, const void *template)
|
||||
int ddsrt_chh_remove (struct ddsrt_chh * __restrict hh, const void * __restrict template)
|
||||
{
|
||||
const uint32_t idxmask = rt->size - 1;
|
||||
uint32_t hopinfo = rt->buckets[bucket].hopinfo;
|
||||
uint32_t idx;
|
||||
for (idx = 0; hopinfo != 0; hopinfo >>= 1, idx++) {
|
||||
const uint32_t bidx = (bucket + idx) & idxmask;
|
||||
void *data = rt->buckets[bidx].data;
|
||||
if (data && rt->equals (data, template))
|
||||
return data;
|
||||
}
|
||||
return NULL;
|
||||
ddsrt_mutex_lock (&hh->lock);
|
||||
int x = ddsrt_hh_remove (&hh->rt, template);
|
||||
ddsrt_mutex_unlock (&hh->lock);
|
||||
return x;
|
||||
}
|
||||
|
||||
void *ddsrt_hh_lookup (const struct ddsrt_hh * __restrict rt, const void * __restrict template)
|
||||
void ddsrt_chh_enum_unsafe (struct ddsrt_chh * __restrict hh, void (*f) (void *a, void *f_arg), void *f_arg)
|
||||
{
|
||||
const uint32_t hash = rt->hash (template);
|
||||
const uint32_t idxmask = rt->size - 1;
|
||||
const uint32_t bucket = hash & idxmask;
|
||||
return ddsrt_hh_lookup_internal (rt, bucket, template);
|
||||
ddsrt_mutex_lock (&hh->lock);
|
||||
ddsrt_hh_enum (&hh->rt, f, f_arg);
|
||||
ddsrt_mutex_unlock (&hh->lock);
|
||||
}
|
||||
|
||||
static uint32_t ddsrt_hh_find_closer_free_bucket (struct ddsrt_hh *rt, uint32_t free_bucket, uint32_t *free_distance)
|
||||
void *ddsrt_chh_iter_first (struct ddsrt_chh * __restrict hh, struct ddsrt_chh_iter *it)
|
||||
{
|
||||
const uint32_t idxmask = rt->size - 1;
|
||||
uint32_t move_bucket, free_dist;
|
||||
move_bucket = (free_bucket - (HH_HOP_RANGE - 1)) & idxmask;
|
||||
for (free_dist = HH_HOP_RANGE - 1; free_dist > 0; free_dist--) {
|
||||
uint32_t move_free_distance = NOT_A_BUCKET;
|
||||
uint32_t mask = 1;
|
||||
uint32_t i;
|
||||
for (i = 0; i < free_dist; i++, mask <<= 1) {
|
||||
if (mask & rt->buckets[move_bucket].hopinfo) {
|
||||
move_free_distance = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (move_free_distance != NOT_A_BUCKET) {
|
||||
uint32_t new_free_bucket = (move_bucket + move_free_distance) & idxmask;
|
||||
rt->buckets[move_bucket].hopinfo |= 1u << free_dist;
|
||||
rt->buckets[free_bucket].data = rt->buckets[new_free_bucket].data;
|
||||
rt->buckets[new_free_bucket].data = NULL;
|
||||
rt->buckets[move_bucket].hopinfo &= ~(1u << move_free_distance);
|
||||
*free_distance -= free_dist - move_free_distance;
|
||||
return new_free_bucket;
|
||||
}
|
||||
move_bucket = (move_bucket + 1) & idxmask;
|
||||
}
|
||||
return NOT_A_BUCKET;
|
||||
ddsrt_mutex_lock (&hh->lock);
|
||||
it->chh = hh;
|
||||
void *x = ddsrt_hh_iter_first (&hh->rt, &it->it);
|
||||
ddsrt_mutex_unlock (&hh->lock);
|
||||
return x;
|
||||
}
|
||||
|
||||
static void ddsrt_hh_resize (struct ddsrt_hh *rt)
|
||||
void *ddsrt_chh_iter_next (struct ddsrt_chh_iter *it)
|
||||
{
|
||||
struct ddsrt_hh_bucket *bs1;
|
||||
uint32_t i, idxmask0, idxmask1;
|
||||
|
||||
bs1 = ddsrt_malloc (2 * rt->size * sizeof (*rt->buckets));
|
||||
|
||||
for (i = 0; i < 2 * rt->size; i++) {
|
||||
bs1[i].hopinfo = 0;
|
||||
bs1[i].data = NULL;
|
||||
}
|
||||
idxmask0 = rt->size - 1;
|
||||
idxmask1 = 2 * rt->size - 1;
|
||||
for (i = 0; i < rt->size; i++) {
|
||||
void *data = rt->buckets[i].data;
|
||||
if (data) {
|
||||
const uint32_t hash = rt->hash (data);
|
||||
const uint32_t old_start_bucket = hash & idxmask0;
|
||||
const uint32_t new_start_bucket = hash & idxmask1;
|
||||
const uint32_t dist = (i >= old_start_bucket) ? (i - old_start_bucket) : (rt->size + i - old_start_bucket);
|
||||
const uint32_t newb = (new_start_bucket + dist) & idxmask1;
|
||||
assert (dist < HH_HOP_RANGE);
|
||||
bs1[new_start_bucket].hopinfo |= 1u << dist;
|
||||
bs1[newb].data = data;
|
||||
}
|
||||
}
|
||||
|
||||
ddsrt_free (rt->buckets);
|
||||
rt->size *= 2;
|
||||
rt->buckets = bs1;
|
||||
ddsrt_mutex_lock (&it->chh->lock);
|
||||
void *x = ddsrt_hh_iter_next (&it->it);
|
||||
ddsrt_mutex_unlock (&it->chh->lock);
|
||||
return x;
|
||||
}
|
||||
|
||||
int ddsrt_hh_add (struct ddsrt_hh * __restrict rt, const void * __restrict data)
|
||||
{
|
||||
const uint32_t hash = rt->hash (data);
|
||||
const uint32_t idxmask = rt->size - 1;
|
||||
const uint32_t start_bucket = hash & idxmask;
|
||||
uint32_t free_distance, free_bucket;
|
||||
|
||||
if (ddsrt_hh_lookup_internal (rt, start_bucket, data)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
free_bucket = start_bucket;
|
||||
for (free_distance = 0; free_distance < HH_ADD_RANGE; free_distance++) {
|
||||
if (rt->buckets[free_bucket].data == NULL)
|
||||
break;
|
||||
free_bucket = (free_bucket + 1) & idxmask;
|
||||
}
|
||||
if (free_distance < HH_ADD_RANGE) {
|
||||
do {
|
||||
if (free_distance < HH_HOP_RANGE) {
|
||||
assert ((uint32_t) free_bucket == ((start_bucket + free_distance) & idxmask));
|
||||
rt->buckets[start_bucket].hopinfo |= 1u << free_distance;
|
||||
rt->buckets[free_bucket].data = (void *) data;
|
||||
return 1;
|
||||
}
|
||||
free_bucket = ddsrt_hh_find_closer_free_bucket (rt, free_bucket, &free_distance);
|
||||
assert (free_bucket == NOT_A_BUCKET || free_bucket <= idxmask);
|
||||
} while (free_bucket != NOT_A_BUCKET);
|
||||
}
|
||||
|
||||
ddsrt_hh_resize (rt);
|
||||
return ddsrt_hh_add (rt, data);
|
||||
}
|
||||
|
||||
int ddsrt_hh_remove (struct ddsrt_hh * __restrict rt, const void * __restrict template)
|
||||
{
|
||||
const uint32_t hash = rt->hash (template);
|
||||
const uint32_t idxmask = rt->size - 1;
|
||||
const uint32_t bucket = hash & idxmask;
|
||||
uint32_t hopinfo;
|
||||
uint32_t idx;
|
||||
hopinfo = rt->buckets[bucket].hopinfo;
|
||||
for (idx = 0; hopinfo != 0; hopinfo >>= 1, idx++) {
|
||||
if (hopinfo & 1) {
|
||||
const uint32_t bidx = (bucket + idx) & idxmask;
|
||||
void *data = rt->buckets[bidx].data;
|
||||
if (data && rt->equals (data, template)) {
|
||||
rt->buckets[bidx].data = NULL;
|
||||
rt->buckets[bucket].hopinfo &= ~(1u << idx);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ddsrt_hh_enum (struct ddsrt_hh * __restrict rt, void (*f) (void *a, void *f_arg), void *f_arg)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < rt->size; i++) {
|
||||
void *data = rt->buckets[i].data;
|
||||
if (data) {
|
||||
f (data, f_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *ddsrt_hh_iter_first (struct ddsrt_hh * __restrict rt, struct ddsrt_hh_iter * __restrict iter)
|
||||
{
|
||||
iter->hh = rt;
|
||||
iter->cursor = 0;
|
||||
return ddsrt_hh_iter_next (iter);
|
||||
}
|
||||
|
||||
void *ddsrt_hh_iter_next (struct ddsrt_hh_iter * __restrict iter)
|
||||
{
|
||||
struct ddsrt_hh *rt = iter->hh;
|
||||
while (iter->cursor < rt->size) {
|
||||
void *data = rt->buckets[iter->cursor].data;
|
||||
iter->cursor++;
|
||||
if (data) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/************* SEQUENTIAL VERSION WITH EMBEDDED DATA ***************/
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue