Files
ldb/memory/mempool.c

316 lines
8.7 KiB
C

#include "mempool.h"
static inline void* page_payload(mp_page_t *pg){
return (void*)((char*)pg + sizeof(mp_page_t));
}
static inline void* large_page_payload(mp_large_t *large){
return (void*)((char*)large + sizeof(mp_large_t));
}
static inline mp_page_t* ptr_to_page(void* p){
return (mp_page_t*)((uintptr_t)p & ~(MEMPOOL_PAGE_SIZE - 1));
}
static inline int size_to_index(size_t size){
if(size == 0 || size > MEMPOOL_BLOCK_MAX_SIZE) return -1;
size_t aligned = (size + 7) & ~7; // 向上8字节对齐
return (aligned >> 3) - 1;
}
// bitmap 操作函数
static inline void bitmap_set(uint64_t *bitmap, uint16_t index){
bitmap[index / 64] |= (1ULL << (index % 64));
}
static inline void bitmap_clear(uint64_t *bitmap, uint16_t index){
bitmap[index / 64] &= ~(1ULL << (index % 64));
}
static inline int bitmap_test(uint64_t *bitmap, uint16_t index){
return (bitmap[index / 64] & (1ULL << (index % 64))) != 0;
}
static inline void bitmap_clear_all(uint64_t *bitmap, size_t size){
memset(bitmap, 0, size * sizeof(uint64_t));
}
// 根据指针计算在页中的块索引
static inline uint16_t ptr_to_block_index(mp_page_t *pg, void *ptr){
char *base = (char*)page_payload(pg);
char *p = (char*)ptr;
size_t offset = p - base;
return (uint16_t)(offset / pg->owner->block_size);
}
static mp_page_t* mp_page_create(mp_bucket_t *owner){
if(!owner) return NULL;
void *mem = NULL;
mem = aligned_alloc(MEMPOOL_PAGE_SIZE, MEMPOOL_PAGE_SIZE);
if(!mem) return NULL;
mp_page_t *pg = (mp_page_t*) mem;
size_t usable = MEMPOOL_PAGE_SIZE - sizeof(mp_page_t);
uint16_t cap = (uint16_t)(usable / owner->block_size);
if(cap == 0) {
free(mem);
return NULL;
}
pg->owner = owner;
pg->capacity = cap;
pg->free_count = cap;
pg->prev = NULL;
pg->next = NULL;
bitmap_clear_all(pg->bitmap, 20);
char *p = (char*)page_payload(pg);
for(uint16_t i = 0;i < cap - 1; ++ i){
*(void**)p = (void*)(p + owner->block_size);
p = p + owner->block_size;
}
*(void**)p = NULL;
pg->free_list = page_payload(pg);
return pg;
}
static void *mp_page_alloc(mp_page_t *pg){
if(!pg || !pg->free_list || pg->free_count == 0) return NULL;
void *ret = pg->free_list;
pg->free_list = *(void**)ret;
pg->free_count --;
// 标记该块为已分配
uint16_t index = ptr_to_block_index(pg, ret);
bitmap_set(pg->bitmap, index);
return ret;
}
static int mp_page_free(mp_page_t *pg, void *ptr){
if(!pg || !ptr) return MEMPOOL_INVALID_INPUT;
// 检查是否是 double free
uint16_t index = ptr_to_block_index(pg, ptr);
if(!bitmap_test(pg->bitmap, index)){
// 该块未被分配,可能是 double free
return MEMPOOL_DOUBLE_FREE;
}
// 标记该块为空闲
bitmap_clear(pg->bitmap, index);
*(void**)ptr = pg->free_list;
pg->free_list = ptr;
pg->free_count ++;
return 0;
}
int mp_create(mp_pool_t *pool){
if(!pool) return MEMPOOL_INVALID_INPUT;
for(int i = 0;i < MEMPOOL_NUM_CLASSES; ++ i){
mp_bucket_t *bucket = &pool->buckets[i];
bucket->block_size = (i+1)*MEMPOOL_ALIGNMENT;
bucket->page_count = 0;
bucket->empty_count = 0;
bucket->empty_pages = bucket->partial_pages = bucket->full_pages = NULL;
}
pool->large_list = NULL;
return 0;
}
int mp_destroy(mp_pool_t *pool){
if(!pool) return MEMPOOL_INVALID_INPUT;
// 释放所有大块内存
mp_large_t *large = pool->large_list;
while(large){
mp_large_t *next = large->next;
free(large);
large = next;
}
// 释放所有页面
for(int i = 0; i < MEMPOOL_NUM_CLASSES; i++){
mp_bucket_t *bucket = &pool->buckets[i];
mp_page_t *lists[] = {bucket->empty_pages, bucket->partial_pages, bucket->full_pages};
for(int j = 0; j < 3; j++){
mp_page_t *pg = lists[j];
while(pg){
mp_page_t *next = pg->next;
free(pg);
pg = next;
}
}
}
return 0;
}
void *mp_alloc(mp_pool_t *pool, size_t size){
if(!pool) return NULL;
if(size > MEMPOOL_BLOCK_MAX_SIZE){
// large_page
void *mem = malloc(size + sizeof(mp_large_t));
if(!mem) return NULL;
mp_large_t *large_page = (mp_large_t*) mem;
large_page->prev = NULL;
large_page->next = pool->large_list;
if (pool->large_list) pool->large_list->prev = large_page;
pool->large_list = large_page;
return large_page_payload(large_page);
}
int i = size_to_index(size);
if(i == -1) return NULL;
mp_bucket_t *bucket = &pool->buckets[i];
if(!bucket->partial_pages){
if(!bucket->empty_pages){
// 无空页,重新分配
mp_page_t *pg = mp_page_create(bucket);
if(!pg) return NULL;
bucket->partial_pages = pg;
bucket->page_count ++;
}else{
// 有空页,使用空页
mp_page_t *epage = bucket->empty_pages;
bucket->empty_pages = epage->next;
if(bucket->empty_pages) bucket->empty_pages->prev = NULL;
epage->next = NULL;
epage->prev = NULL;
bucket->partial_pages = epage;
bucket->empty_count --;
}
}
mp_page_t *pg = bucket->partial_pages;
void *p = mp_page_alloc(pg);
if(!p) return NULL;
if(pg->free_count == 0){
// 该页没有空位
// 从partial链表中取出
if(pg == bucket->partial_pages){
bucket->partial_pages = pg->next;
}
if(pg->prev != NULL) pg->prev->next = pg->next;
if(pg->next != NULL) pg->next->prev = pg->prev;
pg->prev = NULL;
pg->next = NULL;
// 移入 fullpage
if(!bucket->full_pages) {
bucket->full_pages = pg;
}else{
pg->prev = NULL;
pg->next = bucket->full_pages;
if (bucket->full_pages) bucket->full_pages->prev = pg;
bucket->full_pages = pg;
}
}else {
// 不做处理
}
return p;
}
int mp_free(mp_pool_t *pool, void *ptr){
if(!pool || !ptr) return MEMPOOL_INVALID_INPUT;
mp_large_t *large = pool->large_list;
while(large){
if(ptr == large_page_payload(large)){
// 从large链表中删除
if(large == pool->large_list) pool->large_list = large->next;
if(large->prev) large->prev->next = large->next;
if(large->next) large->next->prev = large->prev;
large->prev = NULL;
large->next = NULL;
free(large);
return 0;
}
large = large->next;
}
mp_page_t *pg = ptr_to_page(ptr);
uint16_t before = pg->free_count;
int ret = mp_page_free(pg, ptr);
if(ret != 0){
printf("mempool error\n");
return ret;
}
if(pg->free_count >= pg->capacity){
// free后成为完全空闲页
mp_bucket_t *bucket = pg->owner;
if(bucket->empty_count > MEMPOOL_CACHE_PAGE){
// 空闲页很多
// 摘出partial链表
if(pg == bucket->partial_pages) bucket->partial_pages = pg->next;
if(pg->prev != NULL) pg->prev->next = pg->next;
if(pg->next != NULL) pg->next->prev = pg->prev;
pg->prev = NULL;
pg->next = NULL;
// 释放
free(pg);
bucket->page_count --;
}else{
// 空闲页并不多
// 摘出partial链表
if(pg == bucket->partial_pages) bucket->partial_pages = pg->next;
if(pg->prev != NULL) pg->prev->next = pg->next;
if(pg->next != NULL) pg->next->prev = pg->prev;
pg->prev = NULL;
pg->next = NULL;
// 加入empty链表
pg->next = bucket->empty_pages;
if(bucket->empty_pages) bucket->empty_pages->prev = pg;
bucket->empty_pages = pg;
bucket->empty_count ++;
}
}else if(before == 0 && pg->free_count == 1){
// free 后需要从full移动到partial
mp_bucket_t *bucket = pg->owner;
// 移出full链表
if(pg == bucket->full_pages) bucket->full_pages = pg->next;
if(pg->prev != NULL) pg->prev->next = pg->next;
if(pg->next != NULL) pg->next->prev = pg->prev;
pg->prev = NULL;
pg->next = NULL;
// 加入partial
pg->prev = NULL;
pg->next = bucket->partial_pages;
if (bucket->partial_pages) bucket->partial_pages->prev = pg;
bucket->partial_pages = pg;
}else {
// 有空余空间的页不做处理
}
return 0;
}