/********************************************************************//** 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)); }
/********************************************************************//** Fills the specified free list. @return TRUE if we were able to insert a block to the free list */ static ibool mem_pool_fill_free_list( /*====================*/ ulint i, /*!< in: free list index */ mem_pool_t* pool) /*!< in: memory pool */ { mem_area_t* area; mem_area_t* area2; ibool ret; ut_ad(mutex_own(&(pool->mutex))); if (UNIV_UNLIKELY(i >= 63)) { /* We come here when we have run out of space in the memory pool: */ return(FALSE); } area = UT_LIST_GET_FIRST(pool->free_list[i + 1]); if (area == NULL) { if (UT_LIST_GET_LEN(pool->free_list[i + 1]) > 0) { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Error: mem pool free list %lu" " length is %lu\n" "InnoDB: though the list is empty!\n", (ulong) i + 1, (ulong) UT_LIST_GET_LEN(pool->free_list[i + 1])); } ret = mem_pool_fill_free_list(i + 1, pool); if (ret == FALSE) { return(FALSE); } area = UT_LIST_GET_FIRST(pool->free_list[i + 1]); } if (UNIV_UNLIKELY(UT_LIST_GET_LEN(pool->free_list[i + 1]) == 0)) { mem_analyze_corruption(area); ut_error; } UT_LIST_REMOVE(free_list, pool->free_list[i + 1], area); area2 = (mem_area_t*)(((byte*)area) + ut_2_exp(i)); UNIV_MEM_ALLOC(area2, MEM_AREA_EXTRA_SIZE); mem_area_set_size(area2, ut_2_exp(i)); mem_area_set_free(area2, TRUE); UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area2); mem_area_set_size(area, ut_2_exp(i)); UT_LIST_ADD_FIRST(free_list, pool->free_list[i], area); return(TRUE); }
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); }