/* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the GNU Affero General Public License, version 3 * or later ("AGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "fastcommon/pthread_func.h" #include "fastcommon/logger.h" #include "fastcommon/fc_atomic.h" #include "fastcommon/fast_allocator.h" #include "file_id_hashtable.h" typedef struct file_id_info { string_t file_id; uint32_t hash_code; uint32_t expires; struct { struct file_id_info *htable; struct file_id_info *list; } nexts; } FileIdInfo; typedef struct { FileIdInfo **buckets; uint32_t capacity; #if defined(DEBUG_FLAG) volatile uint32_t count; #endif } FileIdHashtable; typedef struct { pthread_mutex_t *locks; int count; } FileIdSharedLockArray; typedef struct { struct { struct file_id_info *head; struct file_id_info *tail; pthread_mutex_t lock; } list; FileIdHashtable htable; FileIdSharedLockArray lock_array; struct fast_mblock_man allocator; //element: FileIdInfo struct fast_allocator_context acontext; //for string allocator } FileIdHTableContext; static FileIdHTableContext file_id_ctx = { {NULL, NULL}, {NULL, 0}, {NULL, 0} }; static int clear_expired_file_id_func(void *args); int file_id_hashtable_init() { const int obj_size = 0; int result; int bytes; struct fast_region_info regions[2]; pthread_mutex_t *lock; pthread_mutex_t *end; ScheduleArray scheduleArray; ScheduleEntry entry; file_id_ctx.htable.capacity = 1403641; bytes = sizeof(FileIdInfo *) * file_id_ctx.htable.capacity; file_id_ctx.htable.buckets = fc_malloc(bytes); if (file_id_ctx.htable.buckets == NULL) { return ENOMEM; } memset(file_id_ctx.htable.buckets, 0, bytes); file_id_ctx.lock_array.count = 163; bytes = sizeof(pthread_mutex_t) * file_id_ctx.lock_array.count; file_id_ctx.lock_array.locks = fc_malloc(bytes); if (file_id_ctx.lock_array.locks == NULL) { return ENOMEM; } end = file_id_ctx.lock_array.locks + file_id_ctx.lock_array.count; for (lock=file_id_ctx.lock_array.locks; lockstr, file_id->len); FILE_ID_HASHTABLE_SET_BUCKET_AND_LOCK(hash_code); result = 0; PTHREAD_MUTEX_LOCK(lock); previous = NULL; current = *bucket; while (current != NULL) { if (hash_code < current->hash_code) { break; } else if (hash_code == current->hash_code && fc_string_equal( file_id, ¤t->file_id)) { result = EEXIST; break; } previous = current; current = current->nexts.htable; } if (result == 0) { do { if ((finfo=fast_mblock_alloc_object(&file_id_ctx. allocator)) == NULL) { result = ENOMEM; break; } if ((finfo->file_id.str=fast_allocator_alloc(&file_id_ctx. acontext, file_id->len)) == NULL) { fast_mblock_free_object(&file_id_ctx.allocator, finfo); result = ENOMEM; break; } memcpy(finfo->file_id.str, file_id->str, file_id->len); finfo->file_id.len = file_id->len; finfo->hash_code = hash_code; finfo->expires = g_current_time + 3; if (previous == NULL) { finfo->nexts.htable = *bucket; *bucket = finfo; } else { finfo->nexts.htable = current; previous->nexts.htable = finfo; } #if defined(DEBUG_FLAG) FC_ATOMIC_INC(file_id_ctx.htable.count); #endif } while (0); } else { finfo = NULL; } PTHREAD_MUTEX_UNLOCK(lock); if (result == 0) { PTHREAD_MUTEX_LOCK(&file_id_ctx.list.lock); finfo->nexts.list = NULL; if (file_id_ctx.list.tail == NULL) { file_id_ctx.list.head = finfo; } else { file_id_ctx.list.tail->nexts.list = finfo; } file_id_ctx.list.tail = finfo; PTHREAD_MUTEX_UNLOCK(&file_id_ctx.list.lock); } return result; } static int file_id_hashtable_del(FileIdInfo *finfo) { int result; FileIdInfo *current; FileIdInfo *previous; FILE_ID_HASHTABLE_DECLARE_VARS(); FILE_ID_HASHTABLE_SET_BUCKET_AND_LOCK(finfo->hash_code); PTHREAD_MUTEX_LOCK(lock); if (*bucket == NULL) { result = ENOENT; } else if (finfo->hash_code == (*bucket)->hash_code && fc_string_equal(&finfo->file_id, &(*bucket)->file_id)) { *bucket = (*bucket)->nexts.htable; #if defined(DEBUG_FLAG) FC_ATOMIC_DEC(file_id_ctx.htable.count); #endif result = 0; } else { result = ENOENT; previous = *bucket; while ((current=previous->nexts.htable) != NULL) { if (finfo->hash_code < current->hash_code) { break; } else if (finfo->hash_code == current->hash_code && fc_string_equal(&finfo->file_id, ¤t->file_id)) { previous->nexts.htable = current->nexts.htable; #if defined(DEBUG_FLAG) FC_ATOMIC_DEC(file_id_ctx.htable.count); #endif result = 0; break; } previous = current; } } PTHREAD_MUTEX_UNLOCK(lock); return result; } static int clear_expired_file_id_func(void *args) { struct file_id_info *head; struct file_id_info *tail; struct fast_mblock_chain chain; struct fast_mblock_node *node; head = tail = NULL; PTHREAD_MUTEX_LOCK(&file_id_ctx.list.lock); if (file_id_ctx.list.head != NULL && file_id_ctx. list.head->expires < g_current_time) { head = tail = file_id_ctx.list.head; file_id_ctx.list.head = file_id_ctx.list.head->nexts.list; while (file_id_ctx.list.head != NULL && file_id_ctx. list.head->expires < g_current_time) { tail = file_id_ctx.list.head; file_id_ctx.list.head = file_id_ctx.list.head->nexts.list; } if (file_id_ctx.list.head == NULL) { file_id_ctx.list.tail = NULL; } else { tail->nexts.list = NULL; } } PTHREAD_MUTEX_UNLOCK(&file_id_ctx.list.lock); if (head == NULL) { return 0; } chain.head = chain.tail = NULL; do { node = fast_mblock_to_node_ptr(head); if (chain.head == NULL) { chain.head = node; } else { chain.tail->next = node; } chain.tail = node; file_id_hashtable_del(head); fast_allocator_free(&file_id_ctx.acontext, head->file_id.str); } while ((head=head->nexts.list) != NULL); chain.tail->next = NULL; fast_mblock_batch_free(&file_id_ctx.allocator, &chain); return 0; }