odyssey/sources/hashmap.c

258 lines
5.5 KiB
C

/*
* Odyssey.
*
* Scalable PostgreSQL connection pooler.
*/
#include <kiwi.h>
#include <machinarium.h>
#include <odyssey.h>
od_hashmap_list_item_t *od_hashmap_list_item_create(void)
{
od_hashmap_list_item_t *list;
list = malloc(sizeof(od_hashmap_list_item_t));
if (list == NULL) {
return NULL;
}
memset(list, 0, sizeof(od_hashmap_list_item_t));
od_list_init(&list->link);
return list;
}
void od_hashmap_list_item_add(od_hashmap_list_item_t *list,
od_hashmap_list_item_t *it)
{
od_list_append(&list->link, &it->link);
}
od_retcode_t od_hashmap_list_item_free(od_hashmap_list_item_t *l)
{
od_list_unlink(&l->link);
free(l->key.data);
if (l->value.data) {
free(l->value.data);
}
free(l);
return OK_RESPONSE;
}
static inline od_retcode_t od_hash_bucket_init(od_hashmap_bucket_t **b)
{
*b = malloc(sizeof(od_hashmap_bucket_t));
if (*b == NULL) {
return NOT_OK_RESPONSE;
}
pthread_mutex_init(&(*b)->mu, NULL);
(*b)->nodes = od_hashmap_list_item_create();
if ((*b)->nodes == NULL) {
return NOT_OK_RESPONSE;
}
return OK_RESPONSE;
}
static inline od_retcode_t od_hash_bucket_free(od_hashmap_bucket_t *b)
{
pthread_mutex_destroy(&b->mu);
od_hashmap_list_item_free(b->nodes);
free(b);
return OK_RESPONSE;
}
od_hashmap_t *od_hashmap_create(size_t sz)
{
od_hashmap_t *hm;
hm = malloc(sizeof(od_hashmap_t));
if (hm == NULL) {
return NULL;
}
hm->size = sz;
hm->buckets = malloc(sz * sizeof(od_hashmap_bucket_t *));
if (hm->buckets == NULL) {
free(hm);
return NULL;
}
for (size_t i = 0; i < sz; ++i) {
if (od_hash_bucket_init(&hm->buckets[i]) == NOT_OK_RESPONSE) {
free(hm->buckets);
free(hm);
return NULL;
}
}
return hm;
}
od_retcode_t od_hashmap_free(od_hashmap_t *hm)
{
for (size_t i = 0; i < hm->size; ++i) {
od_list_t *j, *n;
od_list_foreach_safe(&hm->buckets[i]->nodes->link, j, n)
{
od_hashmap_list_item_t *it;
it = od_container_of(j, od_hashmap_list_item_t, link);
od_hashmap_list_item_free(it);
}
od_hash_bucket_free(hm->buckets[i]);
}
free(hm->buckets);
free(hm);
return OK_RESPONSE;
}
od_retcode_t od_hashmap_empty(od_hashmap_t *hm)
{
for (size_t i = 0; i < hm->size; ++i) {
pthread_mutex_lock(&hm->buckets[i]->mu);
od_list_t *j, *n;
od_list_foreach_safe(&hm->buckets[i]->nodes->link, j, n)
{
od_hashmap_list_item_t *it;
it = od_container_of(j, od_hashmap_list_item_t, link);
od_hashmap_list_item_free(it);
}
pthread_mutex_unlock(&hm->buckets[i]->mu);
}
return OK_RESPONSE;
}
static inline od_hashmap_elt_t *od_bucket_search(od_hashmap_bucket_t *b,
void *value, size_t value_len)
{
od_list_t *i;
od_list_foreach(&(b->nodes->link), i)
{
od_hashmap_list_item_t *item;
item = od_container_of(i, od_hashmap_list_item_t, link);
if (item->key.len == value_len &&
memcmp(item->key.data, value, value_len) == 0) {
// find
return &item->value;
}
}
return NULL;
}
static inline int od_hashmap_elt_copy(od_hashmap_elt_t *dst,
od_hashmap_elt_t *src)
{
dst->len = src->len;
dst->data = malloc(src->len * sizeof(char));
if (dst->data == NULL) {
return -1;
}
memcpy(dst->data, src->data, src->len);
return 0;
}
int od_hashmap_insert(od_hashmap_t *hm, od_hash_t keyhash,
od_hashmap_elt_t *key, od_hashmap_elt_t **value)
{
size_t bucket_index = keyhash % hm->size;
pthread_mutex_lock(&hm->buckets[bucket_index]->mu);
od_hashmap_elt_t *ptr = od_bucket_search(hm->buckets[bucket_index],
key->data, key->len);
int ret = 1;
if (ptr == NULL) {
od_hashmap_list_item_t *it;
it = od_hashmap_list_item_create();
if (it != NULL) {
od_hashmap_elt_copy(&it->key, key);
od_hashmap_elt_copy(&it->value, *value);
od_hashmap_list_item_add(
hm->buckets[bucket_index]->nodes, it);
ret = 0;
} else {
/* oom or other error */
pthread_mutex_unlock(&hm->buckets[bucket_index]->mu);
return -1;
}
} else {
/* element alrady exists,
* copy *value content to ptr data
* free previous value */
free(ptr->data);
od_hashmap_elt_copy(ptr, *value);
*value = ptr;
}
pthread_mutex_unlock(&hm->buckets[bucket_index]->mu);
return ret;
}
od_hashmap_elt_t *od_hashmap_find(od_hashmap_t *hm, od_hash_t keyhash,
od_hashmap_elt_t *key)
{
size_t bucket_index = keyhash % hm->size;
pthread_mutex_lock(&hm->buckets[bucket_index]->mu);
od_hashmap_elt_t *ptr = od_bucket_search(hm->buckets[bucket_index],
key->data, key->len);
pthread_mutex_unlock(&hm->buckets[bucket_index]->mu);
return ptr;
}
od_hashmap_elt_t *od_hashmap_lock_key(od_hashmap_t *hm, od_hash_t keyhash,
od_hashmap_elt_t *key)
{
size_t bucket_index = keyhash % hm->size;
/*
* This function is used to aquire long locks in auth_query.
* To avoid intra-machine locks we must yield cpu slice from time to time
* even if waiting for other lock.
*/
while (!pthread_mutex_trylock(&hm->buckets[bucket_index]->mu))
machine_sleep(1);
od_hashmap_elt_t *ptr = od_bucket_search(hm->buckets[bucket_index],
key->data, key->len);
if (ptr == NULL) {
od_hashmap_list_item_t *it;
it = od_hashmap_list_item_create();
if (it != NULL) {
od_hashmap_elt_copy(&it->key, key);
od_hashmap_list_item_add(
hm->buckets[bucket_index]->nodes, it);
return &it->value;
} else {
/* oom or other error */
return NULL;
}
} else {
/* element alrady exists, simpty return locked key */
return ptr;
}
}
int od_hashmap_unlock_key(od_hashmap_t *hm, od_hash_t keyhash,
od_hashmap_elt_t *key)
{
(void)key;
size_t bucket_index = keyhash % hm->size;
pthread_mutex_unlock(&hm->buckets[bucket_index]->mu);
return 0 /* OK */;
}