mirror of https://github.com/yandex/odyssey.git
258 lines
5.5 KiB
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 */;
|
|
}
|