/********************************************************************//** Creates a memory pool. @return memory pool */ UNIV_INTERN mem_pool_t* mem_pool_create( /*============*/ ulint size) /*!< in: pool size in bytes */ { mem_pool_t* pool; mem_area_t* area; ulint i; ulint used; pool = ut_malloc(sizeof(mem_pool_t)); pool->buf = ut_malloc_low(size, TRUE); pool->size = size; mutex_create(&pool->mutex, SYNC_MEM_POOL); /* Initialize the free lists */ for (i = 0; i < 64; i++) { UT_LIST_INIT(pool->free_list[i]); } used = 0; while (size - used >= MEM_AREA_MIN_SIZE) { i = ut_2_log(size - used); if (ut_2_exp(i) > size - used) { /* ut_2_log rounds upward */ i--; } area = (mem_area_t*)(pool->buf + used); mem_area_set_size(area, ut_2_exp(i)); mem_area_set_free(area, TRUE); UNIV_MEM_FREE(MEM_AREA_EXTRA_SIZE + (byte*) area, ut_2_exp(i) - MEM_AREA_EXTRA_SIZE); UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area); used = used + ut_2_exp(i); } ut_ad(size >= used); pool->reserved = 0; return(pool); }
/********************************************************************//** Frees memory to a pool. */ UNIV_INTERN void mem_area_free( /*==========*/ void* ptr, /*!< in, own: pointer to allocated memory buffer */ mem_pool_t* pool) /*!< in: memory pool */ { mem_area_t* area; mem_area_t* buddy; void* new_ptr; ulint size; ulint n; if (UNIV_LIKELY(srv_use_sys_malloc)) { free(ptr); return; } /* It may be that the area was really allocated from the OS with regular malloc: check if ptr points within our memory pool */ if ((byte*)ptr < pool->buf || (byte*)ptr >= pool->buf + pool->size) { ut_free(ptr); return; } area = (mem_area_t*) (((byte*)ptr) - MEM_AREA_EXTRA_SIZE); if (mem_area_get_free(area)) { fprintf(stderr, "InnoDB: Error: Freeing element to mem pool" " free list though the\n" "InnoDB: element is marked free!\n"); mem_analyze_corruption(area); ut_error; } size = mem_area_get_size(area); UNIV_MEM_FREE(ptr, size - MEM_AREA_EXTRA_SIZE); if (size == 0) { fprintf(stderr, "InnoDB: Error: Mem area size is 0. Possibly a" " memory overrun of the\n" "InnoDB: previous allocated area!\n"); mem_analyze_corruption(area); ut_error; } #ifdef UNIV_LIGHT_MEM_DEBUG if (((byte*)area) + size < pool->buf + pool->size) { ulint next_size; next_size = mem_area_get_size( (mem_area_t*)(((byte*)area) + size)); if (UNIV_UNLIKELY(!next_size || !ut_is_2pow(next_size))) { fprintf(stderr, "InnoDB: Error: Memory area size %lu," " next area size %lu not a power of 2!\n" "InnoDB: Possibly a memory overrun of" " the buffer being freed here.\n", (ulong) size, (ulong) next_size); mem_analyze_corruption(area); ut_error; } } #endif buddy = mem_area_get_buddy(area, size, pool); n = ut_2_log(size); mem_pool_mutex_enter(pool); mem_n_threads_inside++; ut_a(mem_n_threads_inside == 1); if (buddy && mem_area_get_free(buddy) && (size == mem_area_get_size(buddy))) { /* The buddy is in a free list */ if ((byte*)buddy < (byte*)area) { new_ptr = ((byte*)buddy) + MEM_AREA_EXTRA_SIZE; mem_area_set_size(buddy, 2 * size); mem_area_set_free(buddy, FALSE); } else { new_ptr = ptr; mem_area_set_size(area, 2 * size); } /* Remove the buddy from its free list and merge it to area */ UT_LIST_REMOVE(free_list, pool->free_list[n], buddy); pool->reserved += ut_2_exp(n); mem_n_threads_inside--; mem_pool_mutex_exit(pool); mem_area_free(new_ptr, pool); return; } else { UT_LIST_ADD_FIRST(free_list, pool->free_list[n], area); mem_area_set_free(area, TRUE); ut_ad(pool->reserved >= size); pool->reserved -= size; } mem_n_threads_inside--; mem_pool_mutex_exit(pool); ut_ad(mem_pool_validate(pool)); }
/********************************************************************//** Allocates memory from a pool. NOTE: This low-level function should only be used in mem0mem.*! @return own: allocated memory buffer */ UNIV_INTERN void* mem_area_alloc( /*===========*/ ulint* psize, /*!< in: requested size in bytes; for optimum space usage, the size should be a power of 2 minus MEM_AREA_EXTRA_SIZE; out: allocated size in bytes (greater than or equal to the requested size) */ mem_pool_t* pool) /*!< in: memory pool */ { mem_area_t* area; ulint size; ulint n; ibool ret; /* If we are using os allocator just make a simple call to malloc */ if (UNIV_LIKELY(srv_use_sys_malloc)) { return(malloc(*psize)); } size = *psize; n = ut_2_log(ut_max(size + MEM_AREA_EXTRA_SIZE, MEM_AREA_MIN_SIZE)); mutex_enter(&(pool->mutex)); mem_n_threads_inside++; ut_a(mem_n_threads_inside == 1); area = UT_LIST_GET_FIRST(pool->free_list[n]); if (area == NULL) { ret = mem_pool_fill_free_list(n, pool); if (ret == FALSE) { /* Out of memory in memory pool: we try to allocate from the operating system with the regular malloc: */ mem_n_threads_inside--; mutex_exit(&(pool->mutex)); return(ut_malloc(size)); } area = UT_LIST_GET_FIRST(pool->free_list[n]); } if (!mem_area_get_free(area)) { fprintf(stderr, "InnoDB: Error: Removing element from mem pool" " free list %lu though the\n" "InnoDB: element is not marked free!\n", (ulong) n); mem_analyze_corruption(area); /* Try to analyze a strange assertion failure reported at [email protected] where the free bit IS 1 in the hex dump above */ if (mem_area_get_free(area)) { fprintf(stderr, "InnoDB: Probably a race condition" " because now the area is marked free!\n"); } ut_error; } if (UT_LIST_GET_LEN(pool->free_list[n]) == 0) { fprintf(stderr, "InnoDB: Error: Removing element from mem pool" " free list %lu\n" "InnoDB: though the list length is 0!\n", (ulong) n); mem_analyze_corruption(area); ut_error; } ut_ad(mem_area_get_size(area) == ut_2_exp(n)); mem_area_set_free(area, FALSE); UT_LIST_REMOVE(free_list, pool->free_list[n], area); pool->reserved += mem_area_get_size(area); mem_n_threads_inside--; mutex_exit(&(pool->mutex)); ut_ad(mem_pool_validate(pool)); *psize = ut_2_exp(n) - MEM_AREA_EXTRA_SIZE; UNIV_MEM_ALLOC(MEM_AREA_EXTRA_SIZE + (byte*)area, *psize); return((void*)(MEM_AREA_EXTRA_SIZE + ((byte*)area))); }
mem_pool_t* mem_pool_create( /*============*/ /* out: memory pool */ ulint size) /* in: pool size in bytes */ { mem_pool_t* pool; mem_area_t* area; ulint i; ulint used; ut_a(size > 10000); pool = ut_malloc(sizeof(mem_pool_t)); /* We do not set the memory to zero (FALSE) in the pool, but only when allocated at a higher level in mem0mem.c. This is to avoid masking useful Purify warnings. */ pool->buf = ut_malloc_low(size, FALSE, TRUE); pool->size = size; mutex_create(&pool->mutex, SYNC_MEM_POOL); /* Initialize the free lists */ for (i = 0; i < 64; i++) { UT_LIST_INIT(pool->free_list[i]); } used = 0; while (size - used >= MEM_AREA_MIN_SIZE) { i = ut_2_log(size - used); if (ut_2_exp(i) > size - used) { /* ut_2_log rounds upward */ i--; } area = (mem_area_t*)(pool->buf + used); mem_area_set_size(area, ut_2_exp(i)); mem_area_set_free(area, TRUE); UNIV_MEM_FREE(MEM_AREA_EXTRA_SIZE + (byte*) area, ut_2_exp(i) - MEM_AREA_EXTRA_SIZE); UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area); used = used + ut_2_exp(i); } ut_ad(size >= used); pool->reserved = 0; return(pool); }