summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_mem_cache_pool.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/nxt_mem_cache_pool.c789
1 files changed, 0 insertions, 789 deletions
diff --git a/src/nxt_mem_cache_pool.c b/src/nxt_mem_cache_pool.c
deleted file mode 100644
index 8e843238..00000000
--- a/src/nxt_mem_cache_pool.c
+++ /dev/null
@@ -1,789 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) NGINX, Inc.
- */
-
-#include <nxt_main.h>
-
-
-/*
- * A memory cache pool allocates memory in clusters of specified size and
- * aligned to page_alignment. A cluster is divided on pages of specified
- * size. Page size must be a power of 2. A page can be used entirely or
- * can be divided on chunks of equal size. Chunk size must be a power of 2.
- * A cluster can contains pages with different chunk sizes. Cluster size
- * must be a multiple of page size and may be not a power of 2. Allocations
- * greater than page are allocated outside clusters. Start addresses and
- * sizes of the clusters and large allocations are stored in rbtree blocks
- * to find them on free operations. The rbtree nodes are sorted by start
- * addresses.
- */
-
-
-typedef struct {
- /*
- * Used to link pages with free chunks in pool chunk slot list
- * or to link free pages in clusters.
- */
- nxt_queue_link_t link;
-
- /*
- * Size of chunks or page shifted by pool->chunk_size_shift.
- * Zero means that page is free.
- */
- uint8_t size;
-
- /*
- * Page number in page cluster.
- * There can be no more than 256 pages in a cluster.
- */
- uint8_t number;
-
- /* Number of free chunks of a chunked page. */
- uint8_t chunks;
-
- uint8_t _unused;
-
- /* Chunk bitmap. There can be no more than 32 chunks in a page. */
- uint8_t map[4];
-} nxt_mem_cache_page_t;
-
-
-typedef enum {
- /* Block of cluster. The block is allocated apart of the cluster. */
- NXT_MEM_CACHE_CLUSTER_BLOCK = 0,
- /*
- * Block of large allocation.
- * The block is allocated apart of the allocation.
- */
- NXT_MEM_CACHE_DISCRETE_BLOCK,
- /*
- * Block of large allocation.
- * The block is allocated just after of the allocation.
- */
- NXT_MEM_CACHE_EMBEDDED_BLOCK,
-} nxt_mem_cache_block_type_t;
-
-
-typedef struct {
- NXT_RBTREE_NODE (node);
- nxt_mem_cache_block_type_t type:8;
-
- /* Block size must be less than 4G. */
- uint32_t size;
-
- u_char *start;
- nxt_mem_cache_page_t pages[];
-} nxt_mem_cache_block_t;
-
-
-typedef struct {
- nxt_queue_t pages;
-
- /* Size of page chunks. */
-#if (NXT_64BIT)
- uint32_t size;
-#else
- uint16_t size;
-#endif
-
- /* Maximum number of free chunks in chunked page. */
- uint8_t chunks;
-} nxt_mem_cache_slot_t;
-
-
-struct nxt_mem_cache_pool_s {
- /* rbtree of nxt_mem_cache_block_t. */
- nxt_rbtree_t blocks;
-
- nxt_queue_t free_pages;
-
- uint8_t chunk_size_shift;
- uint8_t page_size_shift;
- uint32_t page_size;
- uint32_t page_alignment;
- uint32_t cluster_size;
-
- nxt_mem_cache_slot_t slots[];
-};
-
-
-#define nxt_mem_cache_chunk_is_free(map, chunk) \
- ((map[chunk / 8] & (0x80 >> (chunk & 7))) == 0)
-
-
-#define nxt_mem_cache_chunk_set_free(map, chunk) \
- map[chunk / 8] &= ~(0x80 >> (chunk & 7))
-
-
-#define nxt_mem_cache_free_junk(p, size) \
- memset((p), 0x5A, size)
-
-
-#define nxt_is_power_of_two(value) \
- ((((value) - 1) & (value)) == 0)
-
-
-static nxt_uint_t nxt_mem_cache_shift(nxt_uint_t n);
-#if !(NXT_DEBUG_MEMORY)
-static void *nxt_mem_cache_alloc_small(nxt_mem_cache_pool_t *pool, size_t size);
-static nxt_uint_t nxt_mem_cache_alloc_chunk(u_char *map, nxt_uint_t size);
-static nxt_mem_cache_page_t *
- nxt_mem_cache_alloc_page(nxt_mem_cache_pool_t *pool);
-static nxt_mem_cache_block_t *
- nxt_mem_cache_alloc_cluster(nxt_mem_cache_pool_t *pool);
-#endif
-static void *nxt_mem_cache_alloc_large(nxt_mem_cache_pool_t *pool,
- size_t alignment, size_t size);
-static intptr_t nxt_mem_cache_rbtree_compare(nxt_rbtree_node_t *node1,
- nxt_rbtree_node_t *node2);
-static nxt_mem_cache_block_t *nxt_mem_cache_find_block(nxt_rbtree_t *tree,
- u_char *p);
-static const char *nxt_mem_cache_chunk_free(nxt_mem_cache_pool_t *pool,
- nxt_mem_cache_block_t *cluster, u_char *p);
-
-
-nxt_mem_cache_pool_t *
-nxt_mem_cache_pool_create(size_t cluster_size, size_t page_alignment,
- size_t page_size, size_t min_chunk_size)
-{
- /* Alignment and sizes must be a power of 2. */
-
- if (nxt_slow_path(!nxt_is_power_of_two(page_alignment)
- || !nxt_is_power_of_two(page_size)
- || !nxt_is_power_of_two(min_chunk_size)))
- {
- return NULL;
- }
-
- page_alignment = nxt_max(page_alignment, NXT_MAX_ALIGNMENT);
-
- if (nxt_slow_path(page_size < 64
- || page_size < page_alignment
- || page_size < min_chunk_size
- || min_chunk_size * 32 < page_size
- || cluster_size < page_size
- || cluster_size / page_size > 256
- || cluster_size % page_size != 0))
- {
- return NULL;
- }
-
- return nxt_mem_cache_pool_fast_create(cluster_size, page_alignment,
- page_size, min_chunk_size);
-}
-
-
-nxt_mem_cache_pool_t *
-nxt_mem_cache_pool_fast_create(size_t cluster_size, size_t page_alignment,
- size_t page_size, size_t min_chunk_size)
-{
- nxt_uint_t slots, chunk_size;
- nxt_mem_cache_slot_t *slot;
- nxt_mem_cache_pool_t *pool;
-
- slots = 0;
- chunk_size = page_size;
-
- do {
- slots++;
- chunk_size /= 2;
- } while (chunk_size > min_chunk_size);
-
- pool = nxt_zalloc(sizeof(nxt_mem_cache_pool_t)
- + slots * sizeof(nxt_mem_cache_slot_t));
-
- if (nxt_fast_path(pool != NULL)) {
- pool->page_size = page_size;
- pool->page_alignment = nxt_max(page_alignment, NXT_MAX_ALIGNMENT);
- pool->cluster_size = cluster_size;
-
- slot = pool->slots;
-
- do {
- nxt_queue_init(&slot->pages);
-
- slot->size = chunk_size;
- /* slot->chunks should be one less than actual number of chunks. */
- slot->chunks = (page_size / chunk_size) - 1;
-
- slot++;
- chunk_size *= 2;
- } while (chunk_size < page_size);
-
- pool->chunk_size_shift = nxt_mem_cache_shift(min_chunk_size);
- pool->page_size_shift = nxt_mem_cache_shift(page_size);
-
- nxt_rbtree_init(&pool->blocks, nxt_mem_cache_rbtree_compare);
-
- nxt_queue_init(&pool->free_pages);
- }
-
- return pool;
-}
-
-
-static nxt_uint_t
-nxt_mem_cache_shift(nxt_uint_t n)
-{
- nxt_uint_t shift;
-
- shift = 0;
- n /= 2;
-
- do {
- shift++;
- n /= 2;
- } while (n != 0);
-
- return shift;
-}
-
-
-nxt_bool_t
-nxt_mem_cache_pool_is_empty(nxt_mem_cache_pool_t *pool)
-{
- return (nxt_rbtree_is_empty(&pool->blocks)
- && nxt_queue_is_empty(&pool->free_pages));
-}
-
-
-void
-nxt_mem_cache_pool_destroy(nxt_mem_cache_pool_t *pool)
-{
- void *p;
- nxt_rbtree_node_t *node, *next;
- nxt_mem_cache_block_t *block;
-
- next = nxt_rbtree_root(&pool->blocks);
-
- while (next != nxt_rbtree_sentinel(&pool->blocks)) {
-
- node = nxt_rbtree_destroy_next(&pool->blocks, &next);
- block = (nxt_mem_cache_block_t *) node;
-
- p = block->start;
-
- if (block->type != NXT_MEM_CACHE_EMBEDDED_BLOCK) {
- nxt_free(block);
- }
-
- nxt_free(p);
- }
-
- nxt_free(pool);
-}
-
-
-void *
-nxt_mem_cache_alloc(nxt_mem_cache_pool_t *pool, size_t size)
-{
-// nxt_debug(task, "mem cache alloc: %zd", size);
-
-#if !(NXT_DEBUG_MEMORY)
-
- if (size <= pool->page_size) {
- return nxt_mem_cache_alloc_small(pool, size);
- }
-
-#endif
-
- return nxt_mem_cache_alloc_large(pool, NXT_MAX_ALIGNMENT, size);
-}
-
-
-void *
-nxt_mem_cache_zalloc(nxt_mem_cache_pool_t *pool, size_t size)
-{
- void *p;
-
- p = nxt_mem_cache_alloc(pool, size);
-
- if (nxt_fast_path(p != NULL)) {
- memset(p, 0, size);
- }
-
- return p;
-}
-
-
-void *
-nxt_mem_cache_align(nxt_mem_cache_pool_t *pool, size_t alignment, size_t size)
-{
-// nxt_debug(task, "mem cache align: @%zd:%zd", alignment, size);
-
- /* Alignment must be a power of 2. */
-
- if (nxt_fast_path(nxt_is_power_of_two(alignment))) {
-
-#if !(NXT_DEBUG_MEMORY)
-
- if (size <= pool->page_size && alignment <= pool->page_alignment) {
- size = nxt_max(size, alignment);
-
- if (size <= pool->page_size) {
- return nxt_mem_cache_alloc_small(pool, size);
- }
- }
-
-#endif
-
- return nxt_mem_cache_alloc_large(pool, alignment, size);
- }
-
- return NULL;
-}
-
-
-void *
-nxt_mem_cache_zalign(nxt_mem_cache_pool_t *pool, size_t alignment, size_t size)
-{
- void *p;
-
- p = nxt_mem_cache_align(pool, alignment, size);
-
- if (nxt_fast_path(p != NULL)) {
- memset(p, 0, size);
- }
-
- return p;
-}
-
-
-#if !(NXT_DEBUG_MEMORY)
-
-nxt_inline u_char *
-nxt_mem_cache_page_addr(nxt_mem_cache_pool_t *pool, nxt_mem_cache_page_t *page)
-{
- nxt_mem_cache_block_t *block;
-
- block = (nxt_mem_cache_block_t *)
- ((u_char *) page - page->number * sizeof(nxt_mem_cache_page_t)
- - offsetof(nxt_mem_cache_block_t, pages));
-
- return block->start + (page->number << pool->page_size_shift);
-}
-
-
-static void *
-nxt_mem_cache_alloc_small(nxt_mem_cache_pool_t *pool, size_t size)
-{
- u_char *p;
- nxt_queue_link_t *link;
- nxt_mem_cache_page_t *page;
- nxt_mem_cache_slot_t *slot;
-
- p = NULL;
-
- if (size <= pool->page_size / 2) {
-
- /* Find a slot with appropriate chunk size. */
- for (slot = pool->slots; slot->size < size; slot++) { /* void */ }
-
- size = slot->size;
-
- if (nxt_fast_path(!nxt_queue_is_empty(&slot->pages))) {
-
- link = nxt_queue_first(&slot->pages);
- page = nxt_queue_link_data(link, nxt_mem_cache_page_t, link);
-
- p = nxt_mem_cache_page_addr(pool, page);
- p += nxt_mem_cache_alloc_chunk(page->map, size);
-
- page->chunks--;
-
- if (page->chunks == 0) {
- /*
- * Remove full page from the pool chunk slot list
- * of pages with free chunks.
- */
- nxt_queue_remove(&page->link);
- }
-
- } else {
- page = nxt_mem_cache_alloc_page(pool);
-
- if (nxt_fast_path(page != NULL)) {
-
- nxt_queue_insert_head(&slot->pages, &page->link);
-
- /* Mark the first chunk as busy. */
- page->map[0] = 0x80;
- page->map[1] = 0;
- page->map[2] = 0;
- page->map[3] = 0;
-
- /* slot->chunks are already one less. */
- page->chunks = slot->chunks;
- page->size = size >> pool->chunk_size_shift;
-
- p = nxt_mem_cache_page_addr(pool, page);
- }
- }
-
- } else {
- page = nxt_mem_cache_alloc_page(pool);
-
- if (nxt_fast_path(page != NULL)) {
- page->size = pool->page_size >> pool->chunk_size_shift;
-
- p = nxt_mem_cache_page_addr(pool, page);
- }
-
-#if (NXT_DEBUG)
- size = pool->page_size;
-#endif
- }
-
-// nxt_debug(task, "mem cache chunk:%uz alloc: %p", size, p);
-
- return p;
-}
-
-
-static nxt_uint_t
-nxt_mem_cache_alloc_chunk(uint8_t *map, nxt_uint_t size)
-{
- uint8_t mask;
- nxt_uint_t n, offset;
-
- offset = 0;
- n = 0;
-
- /* The page must have at least one free chunk. */
-
- for ( ;; ) {
- if (map[n] != 0xff) {
-
- mask = 0x80;
-
- do {
- if ((map[n] & mask) == 0) {
- /* A free chunk is found. */
- map[n] |= mask;
- return offset;
- }
-
- offset += size;
- mask >>= 1;
-
- } while (mask != 0);
-
- } else {
- /* Fast-forward: all 8 chunks are occupied. */
- offset += size * 8;
- }
-
- n++;
- }
-}
-
-
-static nxt_mem_cache_page_t *
-nxt_mem_cache_alloc_page(nxt_mem_cache_pool_t *pool)
-{
- nxt_queue_link_t *link;
- nxt_mem_cache_page_t *page;
- nxt_mem_cache_block_t *cluster;
-
- if (nxt_queue_is_empty(&pool->free_pages)) {
- cluster = nxt_mem_cache_alloc_cluster(pool);
- if (nxt_slow_path(cluster == NULL)) {
- return NULL;
- }
- }
-
- link = nxt_queue_first(&pool->free_pages);
- nxt_queue_remove(link);
-
- page = nxt_queue_link_data(link, nxt_mem_cache_page_t, link);
-
- return page;
-}
-
-
-static nxt_mem_cache_block_t *
-nxt_mem_cache_alloc_cluster(nxt_mem_cache_pool_t *pool)
-{
- nxt_uint_t n;
- nxt_mem_cache_block_t *cluster;
-
- n = pool->cluster_size >> pool->page_size_shift;
-
- cluster = nxt_zalloc(sizeof(nxt_mem_cache_block_t)
- + n * sizeof(nxt_mem_cache_page_t));
-
- if (nxt_slow_path(cluster == NULL)) {
- return NULL;
- }
-
- /* NXT_MEM_CACHE_CLUSTER_BLOCK type is zero. */
-
- cluster->size = pool->cluster_size;
-
- cluster->start = nxt_memalign(pool->page_alignment, pool->cluster_size);
- if (nxt_slow_path(cluster->start == NULL)) {
- nxt_free(cluster);
- return NULL;
- }
-
- n--;
- cluster->pages[n].number = n;
- nxt_queue_insert_head(&pool->free_pages, &cluster->pages[n].link);
-
- while (n != 0) {
- n--;
- cluster->pages[n].number = n;
- nxt_queue_insert_before(&cluster->pages[n + 1].link,
- &cluster->pages[n].link);
- }
-
- nxt_rbtree_insert(&pool->blocks, &cluster->node);
-
- return cluster;
-}
-
-#endif
-
-
-static void *
-nxt_mem_cache_alloc_large(nxt_mem_cache_pool_t *pool, size_t alignment,
- size_t size)
-{
- u_char *p;
- size_t aligned_size;
- uint8_t type;
- nxt_mem_cache_block_t *block;
-
- /* Allocation must be less than 4G. */
- if (nxt_slow_path(size >= 0xffffffff)) {
- return NULL;
- }
-
- if (nxt_is_power_of_two(size)) {
- block = nxt_malloc(sizeof(nxt_mem_cache_block_t));
- if (nxt_slow_path(block == NULL)) {
- return NULL;
- }
-
- p = nxt_memalign(alignment, size);
- if (nxt_slow_path(p == NULL)) {
- nxt_free(block);
- return NULL;
- }
-
- type = NXT_MEM_CACHE_DISCRETE_BLOCK;
-
- } else {
- aligned_size = nxt_align_size(size, sizeof(uintptr_t));
-
- p = nxt_memalign(alignment,
- aligned_size + sizeof(nxt_mem_cache_block_t));
- if (nxt_slow_path(p == NULL)) {
- return NULL;
- }
-
- block = (nxt_mem_cache_block_t *) (p + aligned_size);
- type = NXT_MEM_CACHE_EMBEDDED_BLOCK;
- }
-
- block->type = type;
- block->size = size;
- block->start = p;
-
- nxt_rbtree_insert(&pool->blocks, &block->node);
-
- return p;
-}
-
-
-static intptr_t
-nxt_mem_cache_rbtree_compare(nxt_rbtree_node_t *node1, nxt_rbtree_node_t *node2)
-{
- nxt_mem_cache_block_t *block1, *block2;
-
- block1 = (nxt_mem_cache_block_t *) node1;
- block2 = (nxt_mem_cache_block_t *) node2;
-
- return (uintptr_t) block1->start - (uintptr_t) block2->start;
-}
-
-
-void
-nxt_mem_cache_free(nxt_mem_cache_pool_t *pool, void *p)
-{
- const char *err;
- nxt_mem_cache_block_t *block;
-
-// nxt_debug(task, "mem cache free %p", p);
-
- block = nxt_mem_cache_find_block(&pool->blocks, p);
-
- if (nxt_fast_path(block != NULL)) {
-
- if (block->type == NXT_MEM_CACHE_CLUSTER_BLOCK) {
- err = nxt_mem_cache_chunk_free(pool, block, p);
-
- if (nxt_fast_path(err == NULL)) {
- return;
- }
-
- } else if (nxt_fast_path(p == block->start)) {
- nxt_rbtree_delete(&pool->blocks, &block->node);
-
- if (block->type == NXT_MEM_CACHE_DISCRETE_BLOCK) {
- nxt_free(block);
- }
-
- nxt_free(p);
-
- return;
-
- } else {
- err = "freed pointer points to middle of block: %p";
- }
-
- } else {
- err = "freed pointer is out of pool: %p";
- }
-
-// nxt_log(task, NXT_LOG_CRIT, err, p);
-}
-
-
-static nxt_mem_cache_block_t *
-nxt_mem_cache_find_block(nxt_rbtree_t *tree, u_char *p)
-{
- nxt_rbtree_node_t *node, *sentinel;
- nxt_mem_cache_block_t *block;
-
- node = nxt_rbtree_root(tree);
- sentinel = nxt_rbtree_sentinel(tree);
-
- while (node != sentinel) {
-
- block = (nxt_mem_cache_block_t *) node;
-
- if (p < block->start) {
- node = node->left;
-
- } else if (p >= block->start + block->size) {
- node = node->right;
-
- } else {
- return block;
- }
- }
-
- return NULL;
-}
-
-
-static const char *
-nxt_mem_cache_chunk_free(nxt_mem_cache_pool_t *pool,
- nxt_mem_cache_block_t *cluster, u_char *p)
-{
- u_char *start;
- uintptr_t offset;
- nxt_uint_t n, size, chunk;
- nxt_mem_cache_page_t *page;
- nxt_mem_cache_slot_t *slot;
-
- n = (p - cluster->start) >> pool->page_size_shift;
- start = cluster->start + (n << pool->page_size_shift);
-
- page = &cluster->pages[n];
-
- if (page->size == 0) {
- return "freed pointer points to already free page: %p";
- }
-
- size = page->size << pool->chunk_size_shift;
-
- if (size != pool->page_size) {
-
- offset = (uintptr_t) (p - start) & (pool->page_size - 1);
- chunk = offset / size;
-
- if (nxt_slow_path(offset != chunk * size)) {
- return "freed pointer points to wrong chunk: %p";
- }
-
- if (nxt_slow_path(nxt_mem_cache_chunk_is_free(page->map, chunk))) {
- return "freed pointer points to already free chunk: %p";
- }
-
- nxt_mem_cache_chunk_set_free(page->map, chunk);
-
- /* Find a slot with appropriate chunk size. */
- for (slot = pool->slots; slot->size < size; slot++) { /* void */ }
-
- if (page->chunks != slot->chunks) {
- page->chunks++;
-
- if (page->chunks == 1) {
- /*
- * Add the page to the head of pool chunk slot list
- * of pages with free chunks.
- */
- nxt_queue_insert_head(&slot->pages, &page->link);
- }
-
- nxt_mem_cache_free_junk(p, size);
-
- return NULL;
-
- } else {
- /*
- * All chunks are free, remove the page from pool chunk slot
- * list of pages with free chunks.
- */
- nxt_queue_remove(&page->link);
- }
-
- } else if (nxt_slow_path(p != start)) {
- return "invalid pointer to chunk: %p";
- }
-
- /* Add the free page to the pool's free pages tree. */
-
- page->size = 0;
- nxt_queue_insert_head(&pool->free_pages, &page->link);
-
- nxt_mem_cache_free_junk(p, size);
-
- /* Test if all pages in the cluster are free. */
-
- page = cluster->pages;
- n = pool->cluster_size >> pool->page_size_shift;
-
- do {
- if (page->size != 0) {
- return NULL;
- }
-
- page++;
- n--;
- } while (n != 0);
-
- /* Free cluster. */
-
- page = cluster->pages;
- n = pool->cluster_size >> pool->page_size_shift;
-
- do {
- nxt_queue_remove(&page->link);
- page++;
- n--;
- } while (n != 0);
-
- nxt_rbtree_delete(&pool->blocks, &cluster->node);
-
- p = cluster->start;
-
- nxt_free(cluster);
- nxt_free(p);
-
- return NULL;
-}