/* ********************************************************************************************** * File: maat_rhash.cpp * Description: * Authors: Liu WenTan * Date: 2022-10-31 * Copyright: (c) 2018-2022 Geedge Networks, Inc. All rights reserved. *********************************************************************************************** */ #include #include #include #include #include "rcu_hash.h" #include "utils.h" struct rcu_hash_garbage_bag { void *garbage; void (* garbage_free)(void *garbage); TAILQ_ENTRY(rcu_hash_garbage_bag) entries; }; TAILQ_HEAD(rcu_hash_garbage_q, rcu_hash_garbage_bag); struct rcu_hash_table { int is_updating; char effective_hash; // 'a' or 'b' /* two hash map for rcu */ struct rcu_hash_node *hashmap_a; struct rcu_hash_node *hashmap_b; void (*data_free_fn)(void *user_ctx, void *data); void *user_ctx; struct rcu_hash_garbage_q garbage_q; size_t garbage_q_len; pthread_mutex_t update_mutex; }; struct rcu_hash_node { char *key; size_t key_len; void *data; //table_runtime解析成两个成员 /* htable the node belongs to */ struct rcu_hash_table *htable; UT_hash_handle hh_a; UT_hash_handle hh_b; }; void rcu_hash_garbage_queue_free(struct rcu_hash_garbage_q* garbage_q) { struct rcu_hash_garbage_bag *p = NULL; while ((p = TAILQ_FIRST(garbage_q)) != NULL) { p->garbage_free(p->garbage); TAILQ_REMOVE(garbage_q, p, entries); free(p); } } size_t rcu_hash_garbage_queue_len(struct rcu_hash_table *htable) { return htable->garbage_q_len; } void rcu_hash_garbage_bagging(struct rcu_hash_garbage_q* garbage_q, void* garbage, void (* func)(void *)) { struct rcu_hash_garbage_bag *bag = ALLOC(struct rcu_hash_garbage_bag, 1); bag->garbage = garbage; bag->garbage_free = func; TAILQ_INSERT_TAIL(garbage_q, bag, entries); } void rcu_hash_node_free(struct rcu_hash_node *node) { if (NULL == node) { return; } if (node->key != NULL) { free(node->key); } if (node->data != NULL) { node->htable->data_free_fn(node->htable->user_ctx, node->data); } free(node); } struct rcu_hash_table *rcu_hash_new(rcu_hash_data_free_fn *free_fn) { if (NULL == free_fn) { return NULL; } struct rcu_hash_table *htable = ALLOC(struct rcu_hash_table, 1); htable->is_updating = 0; htable->effective_hash = 'a'; TAILQ_INIT(&htable->garbage_q); htable->garbage_q_len = 0; htable->data_free_fn = free_fn; pthread_mutex_init(&htable->update_mutex, NULL); return htable; } void rcu_hash_free(struct rcu_hash_table *htable) { if (NULL == htable) { return; } struct rcu_hash_node *tmp = NULL; struct rcu_hash_node *item = NULL; if (htable->effective_hash == 'a') { HASH_ITER(hh_a, htable->hashmap_a, item, tmp) { HASH_DELETE(hh_a, htable->hashmap_a, item); rcu_hash_node_free(item); } } else { HASH_ITER(hh_b, htable->hashmap_b, item, tmp) { HASH_DELETE(hh_b, htable->hashmap_b, item); rcu_hash_node_free(item); } } rcu_hash_garbage_queue_free(&(htable->garbage_q)); pthread_mutex_destroy(&htable->update_mutex); free(htable); } void rcu_hash_set_user_ctx(struct rcu_hash_table *htable, void *user_ctx) { htable->user_ctx = user_ctx; } void rcu_hash_commit_prepare(struct rcu_hash_table *htable) { struct rcu_hash_node *node = NULL; struct rcu_hash_node *tmp = NULL; if (htable->effective_hash == 'a') { assert(htable->hashmap_b == NULL); HASH_ITER(hh_a, htable->hashmap_a, node, tmp) { HASH_ADD_KEYPTR(hh_b, htable->hashmap_b, node->key, node->key_len, node); } } else { assert(htable->hashmap_a == NULL); HASH_ITER(hh_b, htable->hashmap_b, node, tmp) { HASH_ADD_KEYPTR(hh_a, htable->hashmap_a, node->key, node->key_len, node); } } htable->is_updating = 1; } void rcu_hash_add(struct rcu_hash_table *htable, const char *key, size_t key_len, void *data) { if (NULL == htable || NULL == key || 0 == key_len) { return; } struct rcu_hash_node *tmp = NULL; struct rcu_hash_node *node = ALLOC(struct rcu_hash_node, 1); node->key = (char *)malloc(sizeof(char) * key_len); memcpy(node->key, key, key_len); node->key_len = key_len; node->data = data; node->htable = htable; if (!htable->is_updating) { rcu_hash_commit_prepare(htable); } if (htable->effective_hash == 'a') { HASH_FIND(hh_b, htable->hashmap_b, key, key_len, tmp); if (NULL == tmp) { HASH_ADD_KEYPTR(hh_b, htable->hashmap_b, key, key_len, node); } } else { HASH_FIND(hh_a, htable->hashmap_a, key, key_len, tmp); if (NULL == tmp) { HASH_ADD_KEYPTR(hh_a, htable->hashmap_a, key, key_len, node); } } } void rcu_hash_del(struct rcu_hash_table *htable, const char *key, size_t key_len) { if (NULL == htable || NULL == key || 0 == key_len) { return; } if (!htable->is_updating) { rcu_hash_commit_prepare(htable); } struct rcu_hash_node *node = NULL; if (htable->effective_hash == 'a') { HASH_FIND(hh_b, htable->hashmap_b, key, key_len, node); if (node != NULL) { HASH_DELETE(hh_b, htable->hashmap_b, node); } } else { HASH_FIND(hh_a, htable->hashmap_a, key, key_len, node); if (node != NULL) { HASH_DELETE(hh_a, htable->hashmap_a, node); } } if (node != NULL) { rcu_hash_garbage_bagging(&(htable->garbage_q), node, (void (*)(void*))rcu_hash_node_free); htable->garbage_q_len++; } } void *rcu_hash_find(struct rcu_hash_table *htable, const char *key, size_t key_len) { if (NULL == htable || NULL == key || 0 == key_len) { return NULL; } struct rcu_hash_node *node = NULL; if (htable->effective_hash == 'a') { HASH_FIND(hh_a, htable->hashmap_a, key, key_len, node); if (node != NULL) { return node->data; } } else { HASH_FIND(hh_b, htable->hashmap_b, key, key_len, node); if (node != NULL) { return node->data; } } return NULL; } size_t rcu_hash_count(struct rcu_hash_table *htable) { if (NULL == htable) { return 0; } if (htable->effective_hash == 'a') { return HASH_CNT(hh_a, htable->hashmap_a); } else { return HASH_CNT(hh_b, htable->hashmap_b); } } void rcu_hash_commit(struct rcu_hash_table *htable) { if (NULL == htable) { return; } if (!htable->is_updating) { return; } struct rcu_hash_node *node = NULL; struct rcu_hash_node *tmp = NULL; pthread_mutex_lock(&htable->update_mutex); if (!htable->is_updating) { pthread_mutex_unlock(&htable->update_mutex); return; } /* updating hash_map is ready, so change effective hash_map */ if (htable->effective_hash == 'a') { htable->effective_hash = 'b'; usleep(100); HASH_ITER(hh_a, htable->hashmap_a, node, tmp) { HASH_DELETE(hh_a, htable->hashmap_a, node); } } else { htable->effective_hash = 'a'; usleep(100); HASH_ITER(hh_b, htable->hashmap_b, node, tmp) { HASH_DELETE(hh_b, htable->hashmap_b, node); } } htable->is_updating = 0; rcu_hash_garbage_queue_free(&(htable->garbage_q)); htable->garbage_q_len = 0; pthread_mutex_unlock(&htable->update_mutex); } size_t rcu_hash_list_updating_data(struct rcu_hash_table *htable, void ***data_array) { size_t i = 0; size_t node_cnt = 0; struct rcu_hash_node *node = NULL, *tmp = NULL; if (htable->effective_hash == 'a') { node_cnt = HASH_CNT(hh_b, htable->hashmap_b); *data_array = ALLOC(void *, node_cnt); HASH_ITER(hh_b, htable->hashmap_b, node, tmp) { (*data_array)[i] = node->data; i++; } } else { node_cnt = HASH_CNT(hh_a, htable->hashmap_a); *data_array = ALLOC(void *, node_cnt); HASH_ITER(hh_a, htable->hashmap_a, node, tmp) { (*data_array)[i] = node->data; i++; } } return node_cnt; } int rcu_hash_updating_flag(struct rcu_hash_table *htable) { return htable->is_updating; }