summaryrefslogtreecommitdiff
path: root/innobase/mem
diff options
context:
space:
mode:
Diffstat (limited to 'innobase/mem')
-rw-r--r--innobase/mem/Makefile.am24
-rw-r--r--innobase/mem/makefilewin10
-rw-r--r--innobase/mem/mem0dbg.c834
-rw-r--r--innobase/mem/mem0mem.c298
-rw-r--r--innobase/mem/mem0pool.c578
-rw-r--r--innobase/mem/ts/makefile12
-rw-r--r--innobase/mem/ts/tsmem.c497
7 files changed, 2253 insertions, 0 deletions
diff --git a/innobase/mem/Makefile.am b/innobase/mem/Makefile.am
new file mode 100644
index 00000000000..89076f76f3b
--- /dev/null
+++ b/innobase/mem/Makefile.am
@@ -0,0 +1,24 @@
+# Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
+# & Innobase Oy
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+include ../include/Makefile.i
+
+libs_LIBRARIES = libmem.a
+
+libmem_a_SOURCES = mem0mem.c mem0pool.c
+
+EXTRA_PROGRAMS =
diff --git a/innobase/mem/makefilewin b/innobase/mem/makefilewin
new file mode 100644
index 00000000000..8a30f8a6e71
--- /dev/null
+++ b/innobase/mem/makefilewin
@@ -0,0 +1,10 @@
+include ..\include\makefile.i
+
+mem.lib: mem0mem.obj mem0pool.obj makefile
+ lib -out:..\libs\mem.lib mem0mem.obj mem0pool.obj
+
+mem0mem.obj: mem0mem.c mem0dbg.c
+ $(CCOM) $(CFL) -c mem0mem.c
+
+mem0pool.obj: mem0pool.c
+ $(CCOM) $(CFL) -c mem0pool.c
diff --git a/innobase/mem/mem0dbg.c b/innobase/mem/mem0dbg.c
new file mode 100644
index 00000000000..0d71708b906
--- /dev/null
+++ b/innobase/mem/mem0dbg.c
@@ -0,0 +1,834 @@
+/************************************************************************
+The memory management: the debug code. This is not a compilation module,
+but is included in mem0mem.* !
+
+(c) 1994, 1995 Innobase Oy
+
+Created 6/9/1994 Heikki Tuuri
+*************************************************************************/
+
+mutex_t mem_hash_mutex; /* The mutex which protects in the
+ debug version the hash table containing
+ the list of live memory heaps, and
+ also the global variables below. */
+
+/* The following variables contain information about the
+extent of memory allocations. Only used in the debug version.
+Protected by mem_hash_mutex above. */
+
+ulint mem_n_created_heaps = 0;
+ulint mem_n_allocations = 0;
+ulint mem_total_allocated_memory = 0;
+ulint mem_current_allocated_memory = 0;
+ulint mem_max_allocated_memory = 0;
+ulint mem_last_print_info = 0;
+
+/* Size of the hash table for memory management tracking */
+#define MEM_HASH_SIZE 997
+
+/* The node of the list containing currently allocated memory heaps */
+
+typedef struct mem_hash_node_struct mem_hash_node_t;
+struct mem_hash_node_struct {
+ UT_LIST_NODE_T(mem_hash_node_t)
+ list; /* hash list node */
+ mem_heap_t* heap; /* memory heap */
+ char* file_name;/* file where heap was created*/
+ ulint line; /* file line of creation */
+ ulint nth_heap;/* this is the nth heap created */
+ UT_LIST_NODE_T(mem_hash_node_t)
+ all_list;/* list of all created heaps */
+};
+
+typedef UT_LIST_BASE_NODE_T(mem_hash_node_t) mem_hash_cell_t;
+
+/* The hash table of allocated heaps */
+mem_hash_cell_t mem_hash_table[MEM_HASH_SIZE];
+
+/* The base node of the list of all allocated heaps */
+mem_hash_cell_t mem_all_list_base;
+
+ibool mem_hash_initialized = FALSE;
+
+
+UNIV_INLINE
+mem_hash_cell_t*
+mem_hash_get_nth_cell(ulint i);
+
+/* Accessor function for the hash table. Returns a pointer to the
+table cell. */
+UNIV_INLINE
+mem_hash_cell_t*
+mem_hash_get_nth_cell(ulint i)
+{
+ ut_a(i < MEM_HASH_SIZE);
+
+ return(&(mem_hash_table[i]));
+}
+
+/* Accessor functions for a memory field in the debug version */
+
+void
+mem_field_header_set_len(byte* field, ulint len)
+{
+ ut_ad(len >= 0);
+
+ mach_write(field - 2 * sizeof(ulint), len);
+}
+
+ulint
+mem_field_header_get_len(byte* field)
+{
+ return(mach_read(field - 2 * sizeof(ulint)));
+}
+
+void
+mem_field_header_set_check(byte* field, ulint check)
+{
+ mach_write(field - sizeof(ulint), check);
+}
+
+ulint
+mem_field_header_get_check(byte* field)
+{
+ return(mach_read(field - sizeof(ulint)));
+}
+
+void
+mem_field_trailer_set_check(byte* field, ulint check)
+{
+ mach_write(field + mem_field_header_get_len(field), check);
+}
+
+ulint
+mem_field_trailer_get_check(byte* field)
+{
+ return(mach_read(field +
+ mem_field_header_get_len(field)));
+}
+
+/**********************************************************************
+Initializes the memory system. */
+
+void
+mem_init(
+/*=====*/
+ ulint size) /* in: common pool size in bytes */
+{
+#ifdef UNIV_MEM_DEBUG
+
+ ulint i;
+
+ /* Initialize the hash table */
+ ut_a(FALSE == mem_hash_initialized);
+
+ mutex_create(&mem_hash_mutex);
+ mutex_set_level(&mem_hash_mutex, SYNC_MEM_HASH);
+
+ for (i = 0; i < MEM_HASH_SIZE; i++) {
+ UT_LIST_INIT(*mem_hash_get_nth_cell(i));
+ }
+
+ UT_LIST_INIT(mem_all_list_base);
+
+ mem_hash_initialized = TRUE;
+#endif
+
+ mem_comm_pool = mem_pool_create(size);
+}
+
+/**********************************************************************
+Initializes an allocated memory field in the debug version. */
+
+void
+mem_field_init(
+/*===========*/
+ byte* buf, /* in: memory field */
+ ulint n) /* in: how many bytes the user requested */
+{
+ ulint rnd;
+ byte* usr_buf;
+
+ usr_buf = buf + MEM_FIELD_HEADER_SIZE;
+
+ /* In the debug version write the length field and the
+ check fields to the start and the end of the allocated storage.
+ The field header consists of a length field and
+ a random number field, in this order. The field trailer contains
+ the same random number as a check field. */
+
+ mem_field_header_set_len(usr_buf, n);
+
+ rnd = ut_rnd_gen_ulint();
+
+ mem_field_header_set_check(usr_buf, rnd);
+ mem_field_trailer_set_check(usr_buf, rnd);
+
+ /* Update the memory allocation information */
+
+ mutex_enter(&mem_hash_mutex);
+
+ mem_total_allocated_memory += n;
+ mem_current_allocated_memory += n;
+ mem_n_allocations++;
+
+ if (mem_current_allocated_memory > mem_max_allocated_memory) {
+ mem_max_allocated_memory = mem_current_allocated_memory;
+ }
+
+ mutex_exit(&mem_hash_mutex);
+
+ /* In the debug version set the buffer to a random
+ combination of 0xBA and 0xBE */
+
+ mem_init_buf(usr_buf, n);
+}
+
+/**********************************************************************
+Erases an allocated memory field in the debug version. */
+
+void
+mem_field_erase(
+/*============*/
+ byte* buf, /* in: memory field */
+ ulint n) /* in: how many bytes the user requested */
+{
+ byte* usr_buf;
+
+ usr_buf = buf + MEM_FIELD_HEADER_SIZE;
+
+ mutex_enter(&mem_hash_mutex);
+ mem_current_allocated_memory -= n;
+ mutex_exit(&mem_hash_mutex);
+
+ /* Check that the field lengths agree */
+ ut_ad(n == (ulint)mem_field_header_get_len(usr_buf));
+
+ /* In the debug version, set the freed space to a random
+ combination of 0xDE and 0xAD */
+
+ mem_erase_buf(buf, MEM_SPACE_NEEDED(n));
+}
+
+/*******************************************************************
+Initializes a buffer to a random combination of hex BA and BE.
+Used to initialize allocated memory. */
+
+void
+mem_init_buf(
+/*=========*/
+ byte* buf, /* in: pointer to buffer */
+ ulint n) /* in: length of buffer */
+{
+ byte* ptr;
+
+ for (ptr = buf; ptr < buf + n; ptr++) {
+
+ if (ut_rnd_gen_ibool()) {
+ *ptr = 0xBA;
+ } else {
+ *ptr = 0xBE;
+ }
+ }
+}
+
+/*******************************************************************
+Initializes a buffer to a random combination of hex DE and AD.
+Used to erase freed memory.*/
+
+void
+mem_erase_buf(
+/*==========*/
+ byte* buf, /* in: pointer to buffer */
+ ulint n) /* in: length of buffer */
+{
+ byte* ptr;
+
+ for (ptr = buf; ptr < buf + n; ptr++) {
+ if (ut_rnd_gen_ibool()) {
+ *ptr = 0xDE;
+ } else {
+ *ptr = 0xAD;
+ }
+ }
+}
+
+/*******************************************************************
+Inserts a created memory heap to the hash table of current allocated
+memory heaps. */
+
+void
+mem_hash_insert(
+/*============*/
+ mem_heap_t* heap, /* in: the created heap */
+ char* file_name, /* in: file name of creation */
+ ulint line) /* in: line where created */
+{
+ mem_hash_node_t* new_node;
+ ulint cell_no ;
+
+ ut_ad(mem_heap_check(heap));
+
+ mutex_enter(&mem_hash_mutex);
+
+ cell_no = ut_hash_ulint((ulint)heap, MEM_HASH_SIZE);
+
+ /* Allocate a new node to the list */
+ new_node = ut_malloc(sizeof(mem_hash_node_t));
+
+ new_node->heap = heap;
+ new_node->file_name = file_name;
+ new_node->line = line;
+ new_node->nth_heap = mem_n_created_heaps;
+
+ /* Insert into lists */
+ UT_LIST_ADD_FIRST(list, *mem_hash_get_nth_cell(cell_no), new_node);
+
+ UT_LIST_ADD_LAST(all_list, mem_all_list_base, new_node);
+
+ mem_n_created_heaps++;
+
+ mutex_exit(&mem_hash_mutex);
+}
+
+/*******************************************************************
+Removes a memory heap (which is going to be freed by the caller)
+from the list of live memory heaps. Returns the size of the heap
+in terms of how much memory in bytes was allocated for the user of
+the heap (not the total space occupied by the heap).
+Also validates the heap.
+NOTE: This function does not free the storage occupied by the
+heap itself, only the node in the list of heaps. */
+
+void
+mem_hash_remove(
+/*============*/
+ mem_heap_t* heap, /* in: the heap to be freed */
+ char* file_name, /* in: file name of freeing */
+ ulint line) /* in: line where freed */
+{
+ mem_hash_node_t* node;
+ ulint cell_no;
+ ibool error;
+ ulint size;
+
+ ut_ad(mem_heap_check(heap));
+
+ mutex_enter(&mem_hash_mutex);
+
+ cell_no = ut_hash_ulint((ulint)heap, MEM_HASH_SIZE);
+
+ /* Look for the heap in the hash table list */
+ node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(cell_no));
+
+ while (node != NULL) {
+ if (node->heap == heap) {
+
+ break;
+ }
+
+ node = UT_LIST_GET_NEXT(list, node);
+ }
+
+ if (node == NULL) {
+ printf(
+ "Memory heap or buffer freed in %s line %lu did not exist.\n",
+ file_name, line);
+ ut_error;
+ }
+
+ /* Remove from lists */
+ UT_LIST_REMOVE(list, *mem_hash_get_nth_cell(cell_no), node);
+
+ UT_LIST_REMOVE(all_list, mem_all_list_base, node);
+
+ /* Validate the heap which will be freed */
+ mem_heap_validate_or_print(node->heap, NULL, FALSE, &error, &size,
+ NULL, NULL);
+ if (error) {
+ printf("Inconsistency in memory heap or buffer n:o %lu created\n",
+ node->nth_heap);
+ printf("in %s line %lu and tried to free in %s line %lu.\n",
+ node->file_name, node->line, file_name, line);
+ ut_error;
+ }
+
+ /* Free the memory occupied by the node struct */
+ ut_free(node);
+
+ mem_current_allocated_memory -= size;
+
+ mutex_exit(&mem_hash_mutex);
+}
+
+/*******************************************************************
+Checks a memory heap for consistency and prints the contents if requested.
+Outputs the sum of sizes of buffers given to the user (only in
+the debug version), the physical size of the heap and the number of
+blocks in the heap. In case of error returns 0 as sizes and number
+of blocks. */
+
+void
+mem_heap_validate_or_print(
+/*=======================*/
+ mem_heap_t* heap, /* in: memory heap */
+ byte* top, /* in: calculate and validate only until
+ this top pointer in the heap is reached,
+ if this pointer is NULL, ignored */
+ ibool print, /* in: if TRUE, prints the contents
+ of the heap; works only in
+ the debug version */
+ ibool* error, /* out: TRUE if error */
+ ulint* us_size,/* out: allocated memory
+ (for the user) in the heap,
+ if a NULL pointer is passed as this
+ argument, it is ignored; in the
+ non-debug version this is always -1 */
+ ulint* ph_size,/* out: physical size of the heap,
+ if a NULL pointer is passed as this
+ argument, it is ignored */
+ ulint* n_blocks) /* out: number of blocks in the heap,
+ if a NULL pointer is passed as this
+ argument, it is ignored */
+{
+ mem_block_t* block;
+ ulint total_len = 0;
+ ulint block_count = 0;
+ ulint phys_len = 0;
+ #ifdef UNIV_MEM_DEBUG
+ ulint len;
+ byte* field;
+ byte* user_field;
+ ulint check_field;
+ #endif
+
+ /* Pessimistically, we set the parameters to error values */
+ if (us_size != NULL) {
+ *us_size = 0;
+ }
+ if (ph_size != NULL) {
+ *ph_size = 0;
+ }
+ if (n_blocks != NULL) {
+ *n_blocks = 0;
+ }
+ *error = TRUE;
+
+ block = heap;
+
+ if (block->magic_n != MEM_BLOCK_MAGIC_N) {
+ return;
+ }
+
+ if (print) {
+ printf("Memory heap:");
+ }
+
+ while (block != NULL) {
+ phys_len += mem_block_get_len(block);
+
+ if ((block->type == MEM_HEAP_BUFFER)
+ && (mem_block_get_len(block) > UNIV_PAGE_SIZE)) {
+
+ /* error */
+
+ return;
+ }
+
+ #ifdef UNIV_MEM_DEBUG
+ /* We can trace the fields of the block only in the debug
+ version */
+ if (print) {
+ printf(" Block %ld:", block_count);
+ }
+
+ field = (byte*)block + mem_block_get_start(block);
+
+ if (top && (field == top)) {
+
+ goto completed;
+ }
+
+ while (field < (byte*)block + mem_block_get_free(block)) {
+
+ /* Calculate the pointer to the storage
+ which was given to the user */
+
+ user_field = field + MEM_FIELD_HEADER_SIZE;
+
+ len = mem_field_header_get_len(user_field);
+
+ if (print) {
+ ut_print_buf(user_field, len);
+ }
+
+ total_len += len;
+ check_field = mem_field_header_get_check(user_field);
+
+ if (check_field !=
+ mem_field_trailer_get_check(user_field)) {
+ /* error */
+
+ return;
+ }
+
+ /* Move to next field */
+ field = field + MEM_SPACE_NEEDED(len);
+
+ if (top && (field == top)) {
+
+ goto completed;
+ }
+
+ }
+
+ /* At the end check that we have arrived to the first free
+ position */
+
+ if (field != (byte*)block + mem_block_get_free(block)) {
+ /* error */
+
+ return;
+ }
+
+ #endif
+
+ block = UT_LIST_GET_NEXT(list, block);
+ block_count++;
+ }
+#ifdef UNIV_MEM_DEBUG
+completed:
+#endif
+ if (us_size != NULL) {
+ *us_size = total_len;
+ }
+ if (ph_size != NULL) {
+ *ph_size = phys_len;
+ }
+ if (n_blocks != NULL) {
+ *n_blocks = block_count;
+ }
+ *error = FALSE;
+}
+
+/******************************************************************
+Prints the contents of a memory heap. */
+
+void
+mem_heap_print(
+/*===========*/
+ mem_heap_t* heap) /* in: memory heap */
+{
+ ibool error;
+ ulint us_size;
+ ulint phys_size;
+ ulint n_blocks;
+
+ ut_ad(mem_heap_check(heap));
+
+ mem_heap_validate_or_print(heap, NULL, TRUE, &error,
+ &us_size, &phys_size, &n_blocks);
+ printf(
+ "\nheap type: %lu; size: user size %lu; physical size %lu; blocks %lu.\n",
+ heap->type, us_size, phys_size, n_blocks);
+ ut_a(!error);
+}
+
+/******************************************************************
+Checks that an object is a memory heap (or a block of it). */
+
+ibool
+mem_heap_check(
+/*===========*/
+ /* out: TRUE if ok */
+ mem_heap_t* heap) /* in: memory heap */
+{
+ ut_a(heap->magic_n == MEM_BLOCK_MAGIC_N);
+
+ return(TRUE);
+}
+
+/******************************************************************
+Validates the contents of a memory heap. */
+
+ibool
+mem_heap_validate(
+/*==============*/
+ /* out: TRUE if ok */
+ mem_heap_t* heap) /* in: memory heap */
+{
+ ibool error;
+ ulint us_size;
+ ulint phys_size;
+ ulint n_blocks;
+
+ ut_ad(mem_heap_check(heap));
+
+ mem_heap_validate_or_print(heap, NULL, FALSE, &error, &us_size,
+ &phys_size, &n_blocks);
+ ut_a(!error);
+
+ return(TRUE);
+}
+
+/*********************************************************************
+Prints information of dynamic memory usage and currently allocated
+memory heaps or buffers. Can only be used in the debug version. */
+static
+void
+mem_print_info_low(
+/*===============*/
+ ibool print_all) /* in: if TRUE, all heaps are printed,
+ else only the heaps allocated after the
+ previous call of this function */
+{
+#ifdef UNIV_MEM_DEBUG
+ mem_hash_node_t* node;
+ ulint n_heaps = 0;
+ ulint allocated_mem;
+ ulint ph_size;
+ ulint total_allocated_mem = 0;
+ ibool error;
+ ulint n_blocks;
+#endif
+ FILE* outfile;
+
+ /* outfile = fopen("ibdebug", "a"); */
+
+ outfile = stdout;
+
+ fprintf(outfile, "\n");
+ fprintf(outfile,
+ "________________________________________________________\n");
+ fprintf(outfile, "MEMORY ALLOCATION INFORMATION\n\n");
+
+#ifndef UNIV_MEM_DEBUG
+
+ mem_pool_print_info(outfile, mem_comm_pool);
+
+ fprintf(outfile,
+ "Sorry, non-debug version cannot give more memory info\n");
+
+ /* fclose(outfile); */
+
+ return;
+#else
+ mutex_enter(&mem_hash_mutex);
+
+ fprintf(outfile, "LIST OF CREATED HEAPS AND ALLOCATED BUFFERS: \n\n");
+
+ if (!print_all) {
+ fprintf(outfile, "AFTER THE LAST PRINT INFO\n");
+ }
+
+ node = UT_LIST_GET_FIRST(mem_all_list_base);
+
+ while (node != NULL) {
+ n_heaps++;
+
+ if (!print_all && node->nth_heap < mem_last_print_info) {
+
+ goto next_heap;
+ }
+
+ mem_heap_validate_or_print(node->heap, NULL,
+ FALSE, &error, &allocated_mem,
+ &ph_size, &n_blocks);
+ total_allocated_mem += allocated_mem;
+
+ fprintf(outfile,
+ "%lu: file %s line %lu of size %lu phys.size %lu with %lu blocks, type %lu\n",
+ node->nth_heap, node->file_name, node->line,
+ allocated_mem, ph_size, n_blocks,
+ (node->heap)->type);
+ next_heap:
+ node = UT_LIST_GET_NEXT(all_list, node);
+ }
+
+ fprintf(outfile, "\n");
+
+ fprintf(outfile, "Current allocated memory : %lu\n",
+ mem_current_allocated_memory);
+ fprintf(outfile, "Current allocated heaps and buffers : %lu\n",
+ n_heaps);
+ fprintf(outfile, "Cumulative allocated memory : %lu\n",
+ mem_total_allocated_memory);
+ fprintf(outfile, "Maximum allocated memory : %lu\n",
+ mem_max_allocated_memory);
+ fprintf(outfile, "Cumulative created heaps and buffers : %lu\n",
+ mem_n_created_heaps);
+ fprintf(outfile, "Cumulative number of allocations : %lu\n",
+ mem_n_allocations);
+
+ mem_last_print_info = mem_n_created_heaps;
+
+ mutex_exit(&mem_hash_mutex);
+
+ mem_pool_print_info(outfile, mem_comm_pool);
+
+ mem_validate();
+
+/* fclose(outfile); */
+#endif
+}
+
+/*********************************************************************
+Prints information of dynamic memory usage and currently allocated memory
+heaps or buffers. Can only be used in the debug version. */
+
+void
+mem_print_info(void)
+/*================*/
+{
+ mem_print_info_low(TRUE);
+}
+
+/*********************************************************************
+Prints information of dynamic memory usage and currently allocated memory
+heaps or buffers since the last ..._print_info or..._print_new_info. */
+
+void
+mem_print_new_info(void)
+/*====================*/
+{
+ mem_print_info_low(FALSE);
+}
+
+/*********************************************************************
+TRUE if no memory is currently allocated. */
+
+ibool
+mem_all_freed(void)
+/*===============*/
+ /* out: TRUE if no heaps exist */
+{
+ #ifdef UNIV_MEM_DEBUG
+
+ mem_hash_node_t* node;
+ ulint heap_count = 0;
+ ulint i;
+
+ mem_validate();
+
+ mutex_enter(&mem_hash_mutex);
+
+ for (i = 0; i < MEM_HASH_SIZE; i++) {
+
+ node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(i));
+ while (node != NULL) {
+ heap_count++;
+ node = UT_LIST_GET_NEXT(list, node);
+ }
+ }
+
+ mutex_exit(&mem_hash_mutex);
+
+ if (heap_count == 0) {
+
+ ut_a(mem_pool_get_reserved(mem_comm_pool) == 0);
+
+ return(TRUE);
+ } else {
+ return(FALSE);
+ }
+
+ #else
+
+ printf(
+ "Sorry, non-debug version cannot check if all memory is freed.\n");
+
+ return(FALSE);
+
+ #endif
+}
+
+/*********************************************************************
+Validates the dynamic memory allocation system. */
+
+ibool
+mem_validate_no_assert(void)
+/*========================*/
+ /* out: TRUE if error */
+{
+ #ifdef UNIV_MEM_DEBUG
+
+ mem_hash_node_t* node;
+ ulint n_heaps = 0;
+ ulint allocated_mem;
+ ulint ph_size;
+ ulint total_allocated_mem = 0;
+ ibool error = FALSE;
+ ulint n_blocks;
+ ulint i;
+
+ mem_pool_validate(mem_comm_pool);
+
+ mutex_enter(&mem_hash_mutex);
+
+ for (i = 0; i < MEM_HASH_SIZE; i++) {
+
+ node = UT_LIST_GET_FIRST(*mem_hash_get_nth_cell(i));
+
+ while (node != NULL) {
+ n_heaps++;
+
+ mem_heap_validate_or_print(node->heap, NULL,
+ FALSE, &error, &allocated_mem,
+ &ph_size, &n_blocks);
+
+ if (error) {
+ printf("\nERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n");
+ printf("Inconsistency in memory heap or buffer created\n");
+ printf("in %s line %lu.\n", node->file_name, node->line);
+
+ mutex_exit(&mem_hash_mutex);
+
+ return(TRUE);
+ }
+
+ total_allocated_mem += allocated_mem;
+ node = UT_LIST_GET_NEXT(list, node);
+ }
+ }
+
+ if ((n_heaps == 0) && (mem_current_allocated_memory != 0)) {
+ error = TRUE;
+ }
+
+ if (mem_total_allocated_memory < mem_current_allocated_memory) {
+ error = TRUE;
+ }
+
+ if (mem_max_allocated_memory > mem_total_allocated_memory) {
+ error = TRUE;
+ }
+
+ if (mem_n_created_heaps < n_heaps) {
+ error = TRUE;
+ }
+
+ mutex_exit(&mem_hash_mutex);
+
+ return(error);
+
+ #else
+
+ printf("Sorry, non-debug version cannot validate dynamic memory\n");
+
+ return(FALSE);
+
+ #endif
+}
+
+/****************************************************************
+Validates the dynamic memory */
+
+ibool
+mem_validate(void)
+/*==============*/
+ /* out: TRUE if ok */
+{
+ ut_a(!mem_validate_no_assert());
+
+ return(TRUE);
+}
diff --git a/innobase/mem/mem0mem.c b/innobase/mem/mem0mem.c
new file mode 100644
index 00000000000..19a2c0d61a7
--- /dev/null
+++ b/innobase/mem/mem0mem.c
@@ -0,0 +1,298 @@
+/************************************************************************
+The memory management
+
+(c) 1994, 1995 Innobase Oy
+
+Created 6/9/1994 Heikki Tuuri
+*************************************************************************/
+
+
+#include "mem0mem.h"
+#ifdef UNIV_NONINL
+#include "mem0mem.ic"
+#endif
+
+#include "mach0data.h"
+#include "buf0buf.h"
+#include "mem0dbg.c"
+#include "btr0sea.h"
+
+/*
+ THE MEMORY MANAGEMENT
+ =====================
+
+The basic element of the memory management is called a memory
+heap. A memory heap is conceptually a
+stack from which memory can be allocated. The stack may grow infinitely.
+The top element of the stack may be freed, or
+the whole stack can be freed at one time. The advantage of the
+memory heap concept is that we can avoid using the malloc and free
+functions of C which are quite expensive, for example, on the Solaris + GCC
+system (50 MHz Sparc, 1993) the pair takes 3 microseconds,
+on Win NT + 100MHz Pentium, 2.5 microseconds.
+When we use a memory heap,
+we can allocate larger blocks of memory at a time and thus
+reduce overhead. Slightly more efficient the method is when we
+allocate the memory from the index page buffer pool, as we can
+claim a new page fast. This is called buffer allocation.
+When we allocate the memory from the dynamic memory of the
+C environment, that is called dynamic allocation.
+
+The default way of operation of the memory heap is the following.
+First, when the heap is created, an initial block of memory is
+allocated. In dynamic allocation this may be about 50 bytes.
+If more space is needed, additional blocks are allocated
+and they are put into a linked list.
+After the initial block, each allocated block is twice the size of the
+previous, until a threshold is attained, after which the sizes
+of the blocks stay the same. An exception is, of course, the case
+where the caller requests a memory buffer whose size is
+bigger than the threshold. In that case a block big enough must
+be allocated.
+
+The heap is physically arranged so that if the current block
+becomes full, a new block is allocated and always inserted in the
+chain of blocks as the last block.
+
+In the debug version of the memory management, all the allocated
+heaps are kept in a list (which is implemented as a hash table).
+Thus we can notice if the caller tries to free an already freed
+heap. In addition, each buffer given to the caller contains
+start field at the start and a trailer field at the end of the buffer.
+
+The start field has the following content:
+A. sizeof(ulint) bytes of field length (in the standard byte order)
+B. sizeof(ulint) bytes of check field (a random number)
+
+The trailer field contains:
+A. sizeof(ulint) bytes of check field (the same random number as at the start)
+
+Thus we can notice if something has been copied over the
+borders of the buffer, which is illegal.
+The memory in the buffers is initialized to a random byte sequence.
+After freeing, all the blocks in the heap are set to random bytes
+to help us discover errors which result from the use of
+buffers in an already freed heap. */
+
+/*******************************************************************
+NOTE: Use the corresponding macro instead of this function.
+Allocates a single buffer of memory from the dynamic memory of
+the C compiler. Is like malloc of C. The buffer must be freed
+with mem_free. */
+
+void*
+mem_alloc_func_noninline(
+/*=====================*/
+ /* out, own: free storage, NULL if did not
+ succeed */
+ ulint n /* in: desired number of bytes */
+ #ifdef UNIV_MEM_DEBUG
+ ,char* file_name, /* in: file name where created */
+ ulint line /* in: line where created */
+ #endif
+ )
+{
+ return(mem_alloc_func(n
+#ifdef UNIV_MEM_DEBUG
+ , file_name, line
+#endif
+ ));
+}
+
+/*******************************************************************
+Creates a memory heap block where data can be allocated. */
+
+mem_block_t*
+mem_heap_create_block(
+/*==================*/
+ /* out, own: memory heap block, NULL if did not
+ succeed */
+ mem_heap_t* heap,/* in: memory heap or NULL if first block should
+ be created */
+ ulint n, /* in: number of bytes needed for user data, or
+ if init_block is not NULL, its size in bytes */
+ void* init_block, /* in: init block in fast create, type must be
+ MEM_HEAP_DYNAMIC */
+ ulint type) /* in: type of heap: MEM_HEAP_DYNAMIC, or
+ MEM_HEAP_BUFFER possibly ORed to MEM_HEAP_BTR_SEARCH */
+{
+ mem_block_t* block;
+ ulint len;
+
+ ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER)
+ || (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH));
+
+ /* In dynamic allocation, calculate the size: block header + data. */
+
+ if (init_block != NULL) {
+ ut_ad(type == MEM_HEAP_DYNAMIC);
+ ut_ad(n > MEM_BLOCK_START_SIZE + MEM_BLOCK_HEADER_SIZE);
+ len = n;
+ block = init_block;
+
+ } else if (type == MEM_HEAP_DYNAMIC) {
+
+ len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
+ block = mem_area_alloc(len, mem_comm_pool);
+ } else {
+ ut_ad(n <= MEM_MAX_ALLOC_IN_BUF);
+
+ len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
+
+ if (len < UNIV_PAGE_SIZE / 2) {
+
+ block = mem_area_alloc(len, mem_comm_pool);
+ } else {
+ len = UNIV_PAGE_SIZE;
+
+ if ((type & MEM_HEAP_BTR_SEARCH) && heap) {
+ /* We cannot allocate the block from the
+ buffer pool, but must get the free block from
+ the heap header free block field */
+
+ block = (mem_block_t*)heap->free_block;
+ heap->free_block = NULL;
+ } else {
+ block = (mem_block_t*)buf_frame_alloc();
+ }
+ }
+ }
+
+ if (block == NULL) {
+
+ return(NULL);
+ }
+
+ block->magic_n = MEM_BLOCK_MAGIC_N;
+
+ mem_block_set_len(block, len);
+ mem_block_set_type(block, type);
+ mem_block_set_free(block, MEM_BLOCK_HEADER_SIZE);
+ mem_block_set_start(block, MEM_BLOCK_HEADER_SIZE);
+
+ block->free_block = NULL;
+
+ if (init_block != NULL) {
+ block->init_block = TRUE;
+ } else {
+ block->init_block = FALSE;
+ }
+
+ ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len);
+
+ return(block);
+}
+
+/*******************************************************************
+Adds a new block to a memory heap. */
+
+mem_block_t*
+mem_heap_add_block(
+/*===============*/
+ /* out: created block, NULL if did not
+ succeed */
+ mem_heap_t* heap, /* in: memory heap */
+ ulint n) /* in: number of bytes user needs */
+{
+ mem_block_t* block;
+ mem_block_t* new_block;
+ ulint new_size;
+
+ ut_ad(mem_heap_check(heap));
+
+ block = UT_LIST_GET_LAST(heap->base);
+
+ /* We have to allocate a new block. The size is always at least
+ doubled until the standard size is reached. After that the size
+ stays the same, except in cases where the caller needs more space. */
+
+ new_size = 2 * mem_block_get_len(block);
+
+ if (heap->type != MEM_HEAP_DYNAMIC) {
+ ut_ad(n <= MEM_MAX_ALLOC_IN_BUF);
+
+ if (new_size > MEM_MAX_ALLOC_IN_BUF) {
+ new_size = MEM_MAX_ALLOC_IN_BUF;
+ }
+ } else if (new_size > MEM_BLOCK_STANDARD_SIZE) {
+
+ new_size = MEM_BLOCK_STANDARD_SIZE;
+ }
+
+ if (new_size < n) {
+ new_size = n;
+ }
+
+ new_block = mem_heap_create_block(heap, new_size, NULL, heap->type);
+
+ if (new_block == NULL) {
+
+ return(NULL);
+ }
+
+ /* Add the new block as the last block */
+
+ UT_LIST_INSERT_AFTER(list, heap->base, block, new_block);
+
+ return(new_block);
+}
+
+/**********************************************************************
+Frees a block from a memory heap. */
+
+void
+mem_heap_block_free(
+/*================*/
+ mem_heap_t* heap, /* in: heap */
+ mem_block_t* block) /* in: block to free */
+{
+ ulint type;
+ ulint len;
+ ibool init_block;
+
+ UT_LIST_REMOVE(list, heap->base, block);
+
+ type = heap->type;
+ len = block->len;
+ init_block = block->init_block;
+
+ #ifdef UNIV_MEM_DEBUG
+ /* In the debug version we set the memory to a random combination
+ of hex 0xDE and 0xAD. */
+
+ mem_erase_buf((byte*)block, len);
+
+ #endif
+
+ if (init_block) {
+ /* Do not have to free: do nothing */
+
+ } else if (type == MEM_HEAP_DYNAMIC) {
+
+ mem_area_free(block, mem_comm_pool);
+ } else {
+ ut_ad(type & MEM_HEAP_BUFFER);
+
+ if (len >= UNIV_PAGE_SIZE / 2) {
+ buf_frame_free((byte*)block);
+ } else {
+ mem_area_free(block, mem_comm_pool);
+ }
+ }
+}
+
+/**********************************************************************
+Frees the free_block field from a memory heap. */
+
+void
+mem_heap_free_block_free(
+/*=====================*/
+ mem_heap_t* heap) /* in: heap */
+{
+ if (heap->free_block) {
+
+ buf_frame_free(heap->free_block);
+
+ heap->free_block = NULL;
+ }
+}
diff --git a/innobase/mem/mem0pool.c b/innobase/mem/mem0pool.c
new file mode 100644
index 00000000000..7418ee36dbc
--- /dev/null
+++ b/innobase/mem/mem0pool.c
@@ -0,0 +1,578 @@
+/************************************************************************
+The lowest-level memory management
+
+(c) 1997 Innobase Oy
+
+Created 5/12/1997 Heikki Tuuri
+*************************************************************************/
+
+#include "mem0pool.h"
+#ifdef UNIV_NONINL
+#include "mem0pool.ic"
+#endif
+
+#include "sync0sync.h"
+#include "ut0mem.h"
+#include "ut0lst.h"
+#include "ut0byte.h"
+
+/* We would like to use also the buffer frames to allocate memory. This
+would be desirable, because then the memory consumption of the database
+would be fixed, and we might even lock the buffer pool to the main memory.
+The problem here is that the buffer management routines can themselves call
+memory allocation, while the buffer pool mutex is reserved.
+
+The main components of the memory consumption are:
+
+1. buffer pool,
+2. parsed and optimized SQL statements,
+3. data dictionary cache,
+4. log buffer,
+5. locks for each transaction,
+6. hash table for the adaptive index,
+7. state and buffers for each SQL query currently being executed,
+8. session for each user, and
+9. stack for each OS thread.
+
+Items 1-3 are managed by an LRU algorithm. Items 5 and 6 can potentially
+consume very much memory. Items 7 and 8 should consume quite little memory,
+and the OS should take care of item 9, which too should consume little memory.
+
+A solution to the memory management:
+
+1. the buffer pool size is set separately;
+2. log buffer size is set separately;
+3. the common pool size for all the other entries, except 8, is set separately.
+
+Problems: we may waste memory if the common pool is set too big. Another
+problem is the locks, which may take very much space in big transactions.
+Then the shared pool size should be set very big. We can allow locks to take
+space from the buffer pool, but the SQL optimizer is then unaware of the
+usable size of the buffer pool. We could also combine the objects in the
+common pool and the buffers in the buffer pool into a single LRU list and
+manage it uniformly, but this approach does not take into account the parsing
+and other costs unique to SQL statements.
+
+So, let the SQL statements and the data dictionary entries form one single
+LRU list, let us call it the dictionary LRU list. The locks for a transaction
+can be seen as a part of the state of the transaction. Hence, they should be
+stored in the common pool. We still have the problem of a very big update
+transaction, for example, which will set very many x-locks on rows, and the
+locks will consume a lot of memory, say, half of the buffer pool size.
+
+Another problem is what to do if we are not able to malloc a requested
+block of memory from the common pool. Then we can truncate the LRU list of
+the dictionary cache. If it does not help, a system error results.
+
+Because 5 and 6 may potentially consume very much memory, we let them grow
+into the buffer pool. We may let the locks of a transaction take frames
+from the buffer pool, when the corresponding memory heap block has grown to
+the size of a buffer frame. Similarly for the hash node cells of the locks,
+and for the adaptive index. Thus, for each individual transaction, its locks
+can occupy at most about the size of the buffer frame of memory in the common
+pool, and after that its locks will grow into the buffer pool. */
+
+/* Memory area header */
+
+struct mem_area_struct{
+ ulint size_and_free; /* memory area size is obtained by
+ anding with ~MEM_AREA_FREE; area in
+ a free list if ANDing with
+ MEM_AREA_FREE results in nonzero */
+ UT_LIST_NODE_T(mem_area_t)
+ free_list; /* free list node */
+};
+
+/* Mask used to extract the free bit from area->size */
+#define MEM_AREA_FREE 1
+
+/* The smallest memory area total size */
+#define MEM_AREA_MIN_SIZE (2 * UNIV_MEM_ALIGNMENT)
+
+/* Data structure for a memory pool. The space is allocated using the buddy
+algorithm, where free list i contains areas of size 2 to power i. */
+
+struct mem_pool_struct{
+ byte* buf; /* memory pool */
+ ulint size; /* memory common pool size */
+ ulint reserved; /* amount of currently allocated
+ memory */
+ mutex_t mutex; /* mutex protecting this struct */
+ UT_LIST_BASE_NODE_T(mem_area_t)
+ free_list[64]; /* lists of free memory areas: an
+ area is put to the list whose number
+ is the 2-logarithm of the area size */
+};
+
+/* The common memory pool */
+mem_pool_t* mem_comm_pool = NULL;
+
+ulint mem_out_of_mem_err_msg_count = 0;
+
+/************************************************************************
+Returns memory area size. */
+UNIV_INLINE
+ulint
+mem_area_get_size(
+/*==============*/
+ /* out: size */
+ mem_area_t* area) /* in: area */
+{
+ return(area->size_and_free & ~MEM_AREA_FREE);
+}
+
+/************************************************************************
+Sets memory area size. */
+UNIV_INLINE
+void
+mem_area_set_size(
+/*==============*/
+ mem_area_t* area, /* in: area */
+ ulint size) /* in: size */
+{
+ area->size_and_free = (area->size_and_free & MEM_AREA_FREE)
+ | size;
+}
+
+/************************************************************************
+Returns memory area free bit. */
+UNIV_INLINE
+ibool
+mem_area_get_free(
+/*==============*/
+ /* out: TRUE if free */
+ mem_area_t* area) /* in: area */
+{
+ ut_ad(TRUE == MEM_AREA_FREE);
+
+ return(area->size_and_free & MEM_AREA_FREE);
+}
+
+/************************************************************************
+Sets memory area free bit. */
+UNIV_INLINE
+void
+mem_area_set_free(
+/*==============*/
+ mem_area_t* area, /* in: area */
+ ibool free) /* in: free bit value */
+{
+ ut_ad(TRUE == MEM_AREA_FREE);
+
+ area->size_and_free = (area->size_and_free & ~MEM_AREA_FREE)
+ | free;
+}
+
+/************************************************************************
+Creates a memory pool. */
+
+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));
+
+ pool->buf = ut_malloc(size);
+ pool->size = size;
+
+ mutex_create(&(pool->mutex));
+ mutex_set_level(&(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);
+
+ 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);
+}
+
+/************************************************************************
+Fills the specified free list. */
+static
+ibool
+mem_pool_fill_free_list(
+/*====================*/
+ /* out: TRUE if we were able to insert a
+ block to the 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 (i >= 63) {
+ /* We come here when we have run out of space in the
+ memory pool: */
+
+ if (mem_out_of_mem_err_msg_count % 1000 == 0) {
+ /* We do not print the message every time: */
+
+ fprintf(stderr,
+ "Innobase: Warning: out of memory in additional memory pool.\n");
+ fprintf(stderr,
+ "Innobase: Innobase will start allocating memory from the OS.\n");
+ fprintf(stderr,
+ "Innobase: You should restart the database with a bigger value in\n");
+ fprintf(stderr,
+ "Innobase: the MySQL .cnf file for innobase_additional_mem_pool_size.\n");
+ }
+
+ mem_out_of_mem_err_msg_count++;
+
+ return(FALSE);
+ }
+
+ area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
+
+ if (area == NULL) {
+ ret = mem_pool_fill_free_list(i + 1, pool);
+
+ if (ret == FALSE) {
+ return(FALSE);
+ }
+
+ area = UT_LIST_GET_FIRST(pool->free_list[i + 1]);
+ }
+
+ UT_LIST_REMOVE(free_list, pool->free_list[i + 1], area);
+
+ area2 = (mem_area_t*)(((byte*)area) + ut_2_exp(i));
+
+ 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);
+}
+
+/************************************************************************
+Allocates memory from a pool. NOTE: This low-level function should only be
+used in mem0mem.*! */
+
+void*
+mem_area_alloc(
+/*===========*/
+ /* out, own: allocated memory buffer */
+ ulint size, /* in: allocated size in bytes; for optimum
+ space usage, the size should be a power of 2
+ minus MEM_AREA_EXTRA_SIZE */
+ mem_pool_t* pool) /* in: memory pool */
+{
+ mem_area_t* area;
+ ulint n;
+ ibool ret;
+
+ n = ut_2_log(ut_max(size + MEM_AREA_EXTRA_SIZE, MEM_AREA_MIN_SIZE));
+
+ mutex_enter(&(pool->mutex));
+
+ 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: */
+
+ mutex_exit(&(pool->mutex));
+
+ return(ut_malloc(size));
+ }
+
+ area = UT_LIST_GET_FIRST(pool->free_list[n]);
+ }
+
+ ut_a(mem_area_get_free(area));
+ 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);
+
+ mutex_exit(&(pool->mutex));
+
+ ut_ad(mem_pool_validate(pool));
+
+ return((void*)(MEM_AREA_EXTRA_SIZE + ((byte*)area)));
+}
+
+/************************************************************************
+Gets the buddy of an area, if it exists in pool. */
+UNIV_INLINE
+mem_area_t*
+mem_area_get_buddy(
+/*===============*/
+ /* out: the buddy, NULL if no buddy in pool */
+ mem_area_t* area, /* in: memory area */
+ ulint size, /* in: memory area size */
+ mem_pool_t* pool) /* in: memory pool */
+{
+ mem_area_t* buddy;
+
+ ut_ad(size != 0);
+
+ if (((((byte*)area) - pool->buf) % (2 * size)) == 0) {
+
+ /* The buddy is in a higher address */
+
+ buddy = (mem_area_t*)(((byte*)area) + size);
+
+ if ((((byte*)buddy) - pool->buf) + size > pool->size) {
+
+ /* The buddy is not wholly contained in the pool:
+ there is no buddy */
+
+ buddy = NULL;
+ }
+ } else {
+ /* The buddy is in a lower address; NOTE that area cannot
+ be at the pool lower end, because then we would end up to
+ the upper branch in this if-clause: the remainder would be
+ 0 */
+
+ buddy = (mem_area_t*)(((byte*)area) - size);
+ }
+
+ return(buddy);
+}
+
+/************************************************************************
+Frees memory to a pool. */
+
+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 (mem_out_of_mem_err_msg_count > 0) {
+ /* 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);
+
+ size = mem_area_get_size(area);
+
+ ut_ad(size != 0);
+ ut_a(!mem_area_get_free(area));
+
+#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));
+ ut_a(ut_2_power_up(next_size) == next_size);
+ }
+#endif
+ buddy = mem_area_get_buddy(area, size, pool);
+
+ n = ut_2_log(size);
+
+ mutex_enter(&(pool->mutex));
+
+ 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);
+
+ mutex_exit(&(pool->mutex));
+
+ 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;
+ }
+
+ mutex_exit(&(pool->mutex));
+
+ ut_ad(mem_pool_validate(pool));
+}
+
+/************************************************************************
+Validates a memory pool. */
+
+ibool
+mem_pool_validate(
+/*==============*/
+ /* out: TRUE if ok */
+ mem_pool_t* pool) /* in: memory pool */
+{
+ mem_area_t* area;
+ mem_area_t* buddy;
+ ulint free;
+ ulint i;
+
+ mutex_enter(&(pool->mutex));
+
+ free = 0;
+
+ for (i = 0; i < 64; i++) {
+
+ UT_LIST_VALIDATE(free_list, mem_area_t, pool->free_list[i]);
+
+ area = UT_LIST_GET_FIRST(pool->free_list[i]);
+
+ while (area != NULL) {
+ ut_a(mem_area_get_free(area));
+ ut_a(mem_area_get_size(area) == ut_2_exp(i));
+
+ buddy = mem_area_get_buddy(area, ut_2_exp(i), pool);
+
+ ut_a(!buddy || !mem_area_get_free(buddy)
+ || (ut_2_exp(i) != mem_area_get_size(buddy)));
+
+ area = UT_LIST_GET_NEXT(free_list, area);
+
+ free += ut_2_exp(i);
+ }
+ }
+
+ ut_a(free + pool->reserved == pool->size
+ - (pool->size % MEM_AREA_MIN_SIZE));
+ mutex_exit(&(pool->mutex));
+
+ return(TRUE);
+}
+
+/************************************************************************
+Prints info of a memory pool. */
+
+void
+mem_pool_print_info(
+/*================*/
+ FILE* outfile,/* in: output file to write to */
+ mem_pool_t* pool) /* in: memory pool */
+{
+ ulint i;
+
+ mem_pool_validate(pool);
+
+ fprintf(outfile, "INFO OF A MEMORY POOL\n");
+
+ mutex_enter(&(pool->mutex));
+
+ for (i = 0; i < 64; i++) {
+ if (UT_LIST_GET_LEN(pool->free_list[i]) > 0) {
+
+ fprintf(outfile,
+ "Free list length %lu for blocks of size %lu\n",
+ UT_LIST_GET_LEN(pool->free_list[i]),
+ ut_2_exp(i));
+ }
+ }
+
+ fprintf(outfile, "Pool size %lu, reserved %lu.\n", pool->size,
+ pool->reserved);
+ mutex_exit(&(pool->mutex));
+}
+
+/************************************************************************
+Returns the amount of reserved memory. */
+
+ulint
+mem_pool_get_reserved(
+/*==================*/
+ /* out: reserved mmeory in bytes */
+ mem_pool_t* pool) /* in: memory pool */
+{
+ ulint reserved;
+
+ mutex_enter(&(pool->mutex));
+
+ reserved = pool->reserved;
+
+ mutex_exit(&(pool->mutex));
+
+ return(reserved);
+}
diff --git a/innobase/mem/ts/makefile b/innobase/mem/ts/makefile
new file mode 100644
index 00000000000..0f6855322ce
--- /dev/null
+++ b/innobase/mem/ts/makefile
@@ -0,0 +1,12 @@
+
+include ..\..\makefile.i
+
+tsmem: ..\mem.lib tsmem.c makefile
+ $(CCOM) $(CFL) -I.. -I..\.. ..\mem.lib ..\..\btr.lib ..\..\trx.lib ..\..\pars.lib ..\..\que.lib ..\..\lock.lib ..\..\row.lib ..\..\read.lib ..\..\srv.lib ..\..\com.lib ..\..\usr.lib ..\..\thr.lib ..\..\fut.lib ..\..\fsp.lib ..\..\page.lib ..\..\dyn.lib ..\..\mtr.lib ..\..\log.lib ..\..\rem.lib ..\..\fil.lib ..\..\buf.lib ..\..\dict.lib ..\..\data.lib ..\..\mach.lib ..\..\ha.lib ..\..\ut.lib ..\..\sync.lib ..\..\os.lib tsmem.c $(LFL)
+
+
+
+
+
+
+
diff --git a/innobase/mem/ts/tsmem.c b/innobase/mem/ts/tsmem.c
new file mode 100644
index 00000000000..4a108251673
--- /dev/null
+++ b/innobase/mem/ts/tsmem.c
@@ -0,0 +1,497 @@
+/************************************************************************
+The test module for the memory management of Innobase
+
+(c) 1994, 1995 Innobase Oy
+
+Created 6/10/1994 Heikki Tuuri
+*************************************************************************/
+
+#include "../mem0mem.h"
+#include "sync0sync.h"
+#include "ut0rnd.h"
+
+mem_heap_t* heap_arr[1200];
+
+byte* buf_arr[10000];
+ulint rnd_arr[10000];
+
+
+#ifdef UNIV_DEBUG
+/*********************************************************************
+Debug version test. */
+
+void
+test1(void)
+/*=======*/
+{
+ mem_heap_t* heap_1, *heap_2;
+ byte* buf_1, *buf_2, *buf_3;
+ byte check;
+ bool error;
+ ulint i;
+ ulint j;
+ ulint sum;
+ ulint user_size;
+ ulint phys_size, phys_size_1, phys_size_2;
+ ulint n_blocks;
+ ulint p;
+ byte block[1024];
+ byte* top_1, *top_2;
+
+ /* For this test to work the memory alignment must be
+ even (presumably a reasonable assumption) */
+ ut_a(0 == (UNIV_MEM_ALIGNMENT & 1));
+
+ printf("-------------------------------------------\n");
+ printf("TEST 1. Basic test \n");
+
+ heap_1 = mem_heap_create(0);
+
+ buf_1 = mem_heap_alloc(heap_1, 11);
+
+ heap_2 = mem_heap_create(0);
+
+ buf_2 = mem_heap_alloc(heap_1, 15);
+
+ /* Test that the field is properly initialized */
+ for (i = 0; i < 11; i++) {
+ ut_a((*(buf_1 + i) == 0xBA) || (*(buf_1 + i) == 0xBE));
+ }
+
+ check = *(buf_1 + 11);
+
+ mem_validate();
+
+ /* Make an advertent error in the heap */
+ (*(buf_1 + 11))++;
+
+ error = mem_validate_no_assert();
+
+ ut_a(error);
+
+ /* Fix the error in heap before freeing */
+ *(buf_1 + 11) = check;
+
+ mem_print_info();
+
+ /* Free the top buffer buf_2 */
+ mem_heap_free_top(heap_1, 15);
+
+ /* Test that the field is properly erased */
+ for (i = 0; i < 15; i++) {
+ ut_a((*(buf_2 + i) == 0xDE) || (*(buf_2 + i) == 0xAD));
+ }
+
+ /* Test that a new buffer is allocated from the same position
+ as buf_2 */
+ buf_3 = mem_heap_alloc(heap_1, 15);
+
+ ut_a(buf_3 == buf_2);
+
+ mem_heap_free(heap_1);
+
+ /* Test that the field is properly erased */
+ for (i = 0; i < 11; i++) {
+ ut_a((*(buf_1 + i) == 0xDE) || (*(buf_1 + i) == 0xAD));
+ }
+
+ mem_validate();
+
+ mem_print_info();
+
+ printf("-------------------------------------------\n");
+ printf("TEST 2. Test of massive allocation and freeing\n");
+
+ sum = 0;
+ for (i = 0; i < 10000; i++) {
+
+ j = ut_rnd_gen_ulint() % 16 + 15;
+
+ sum = sum + j;
+
+ buf_1 = mem_heap_alloc(heap_2, j);
+ rnd_arr[i] = j;
+
+ buf_arr[i] = buf_1;
+
+ ut_a(buf_1 == mem_heap_get_top(heap_2, j));
+ }
+
+ mem_heap_validate_or_print(heap_2, NULL, FALSE, &error, &user_size,
+ &phys_size_1,
+ &n_blocks);
+
+ ut_a(!error);
+ ut_a(user_size == sum);
+
+ (*(buf_1 - 1))++;
+
+ ut_a(mem_validate_no_assert());
+
+ (*(buf_1 - 1))--;
+
+ mem_print_info();
+
+
+ for (p = 10000; p > 0 ; p--) {
+
+ j = rnd_arr[p - 1];
+
+ ut_a(buf_arr[p - 1] == mem_heap_get_top(heap_2, j));
+ mem_heap_free_top(heap_2, j);
+ }
+
+ mem_print_info();
+
+ mem_heap_free(heap_2);
+
+ mem_print_info();
+
+ printf("-------------------------------------------\n");
+ printf("TEST 3. More tests on the validating \n");
+
+ heap_1 = mem_heap_create(UNIV_MEM_ALIGNMENT * 20);
+
+ buf_1 = mem_heap_alloc(heap_1, UNIV_MEM_ALIGNMENT * 20);
+
+ mem_heap_validate_or_print(heap_1, NULL, FALSE, &error, &user_size,
+ &phys_size_1,
+ &n_blocks);
+
+ ut_a((ulint)(buf_1 - (byte*)heap_1) == (MEM_BLOCK_HEADER_SIZE
+ + MEM_FIELD_HEADER_SIZE));
+
+ mem_validate();
+
+ mem_print_info();
+
+ ut_a(user_size == UNIV_MEM_ALIGNMENT * 20);
+ ut_a(phys_size_1 == (ulint)(ut_calc_align(MEM_FIELD_HEADER_SIZE
+ + UNIV_MEM_ALIGNMENT * 20
+ + MEM_FIELD_TRAILER_SIZE,
+ UNIV_MEM_ALIGNMENT)
+ + MEM_BLOCK_HEADER_SIZE));
+
+ ut_a(n_blocks == 1);
+
+ buf_2 = mem_heap_alloc(heap_1, UNIV_MEM_ALIGNMENT * 3 - 1);
+
+ mem_heap_validate_or_print(heap_1, NULL, FALSE, &error,
+ &user_size, &phys_size_2,
+ &n_blocks);
+
+ printf("Physical size of the heap %ld\n", phys_size_2);
+
+ ut_a(!error);
+ ut_a(user_size == UNIV_MEM_ALIGNMENT * 23 - 1);
+ ut_a(phys_size_2 == (ulint) (phys_size_1
+ + ut_calc_align(MEM_FIELD_HEADER_SIZE
+ + phys_size_1 * 2
+ + MEM_FIELD_TRAILER_SIZE,
+ UNIV_MEM_ALIGNMENT)
+ + MEM_BLOCK_HEADER_SIZE));
+
+ ut_a(n_blocks == 2);
+
+ buf_3 = mem_heap_alloc(heap_1, UNIV_MEM_ALIGNMENT * 3 + 5);
+
+ ut_a((ulint)(buf_3 - buf_2) == ut_calc_align(
+ (UNIV_MEM_ALIGNMENT * 3
+ + MEM_FIELD_TRAILER_SIZE),
+ UNIV_MEM_ALIGNMENT)
+ + MEM_FIELD_HEADER_SIZE);
+
+
+ ut_memcpy(buf_3, buf_2, UNIV_MEM_ALIGNMENT * 3);
+
+ mem_heap_validate_or_print(heap_1, NULL, FALSE, &error,
+ &user_size, &phys_size,
+ &n_blocks);
+
+ ut_a(!error);
+ ut_a(user_size == UNIV_MEM_ALIGNMENT * 26 + 4);
+ ut_a(phys_size == phys_size_2);
+ ut_a(n_blocks == 2);
+
+
+ /* Make an advertent error to buf_3 */
+
+ (*(buf_3 - 1))++;
+
+ mem_heap_validate_or_print(heap_1, NULL, FALSE, &error,
+ &user_size, &phys_size,
+ &n_blocks);
+
+ ut_a(error);
+ ut_a(user_size == 0);
+ ut_a(phys_size == 0);
+ ut_a(n_blocks == 0);
+
+ /* Fix the error and make another */
+
+ (*(buf_3 - 1))--;
+ (*(buf_3 + UNIV_MEM_ALIGNMENT * 3 + 5))++;
+
+ mem_heap_validate_or_print(heap_1, NULL, FALSE, &error,
+ &user_size, &phys_size,
+ &n_blocks);
+
+ ut_a(error);
+
+ (*(buf_3 + UNIV_MEM_ALIGNMENT * 3 + 5))--;
+
+ buf_1 = mem_heap_alloc(heap_1, UNIV_MEM_ALIGNMENT + 4);
+
+ ut_a((ulint)(buf_1 - buf_3) == ut_calc_align(UNIV_MEM_ALIGNMENT * 3 + 5
+ + MEM_FIELD_TRAILER_SIZE ,
+ UNIV_MEM_ALIGNMENT)
+ + MEM_FIELD_HEADER_SIZE);
+
+
+ mem_heap_validate_or_print(heap_1, NULL, FALSE, &error,
+ &user_size, &phys_size,
+ &n_blocks);
+
+ ut_a(!error);
+ ut_a(user_size == UNIV_MEM_ALIGNMENT * 27 + 8);
+ ut_a(phys_size == phys_size_2);
+ ut_a(n_blocks == 2);
+
+
+ mem_print_info();
+
+ mem_heap_free(heap_1);
+
+ printf("-------------------------------------------\n");
+ printf("TEST 4. Test of massive allocation \n");
+ printf("of heaps to test the hash table\n");
+
+ for (i = 0; i < 500; i++) {
+ heap_arr[i] = mem_heap_create(i);
+ buf_2 = mem_heap_alloc(heap_arr[i], 2 * i);
+ }
+
+ mem_validate();
+
+ for (i = 0; i < 500; i++) {
+ mem_heap_free(heap_arr[i]);
+ }
+
+ mem_validate();
+
+ mem_print_info();
+
+ /* Validating a freed heap should generate an error */
+
+ mem_heap_validate_or_print(heap_1, NULL, FALSE, &error,
+ NULL, NULL, NULL);
+
+ ut_a(error);
+
+ printf("-------------------------------------------\n");
+ printf("TEST 5. Test of mem_alloc and mem_free \n");
+
+ buf_1 = mem_alloc(11100);
+ buf_2 = mem_alloc(23);
+
+ ut_memcpy(buf_2, buf_1, 23);
+
+ mem_validate();
+
+ mem_print_info();
+
+ mem_free(buf_1);
+ mem_free(buf_2);
+
+ mem_validate();
+
+ printf("-------------------------------------------\n");
+ printf("TEST 6. Test of mem_heap_print \n");
+
+ heap_1 = mem_heap_create(0);
+
+ buf_1 = mem_heap_alloc(heap_1, 7);
+
+ ut_memcpy(buf_1, "Pascal", 7);
+
+ for (i = 0; i < 10; i++) {
+ buf_1 = mem_heap_alloc(heap_1, 6);
+ ut_memcpy(buf_1, "Cobol", 6);
+ }
+
+ printf("A heap with 1 Pascal and 10 Cobol's\n");
+ mem_heap_print(heap_1);
+
+ for (i = 0; i < 10; i++) {
+ mem_heap_free_top(heap_1, 6);
+ }
+
+ printf("A heap with 1 Pascal and 0 Cobol's\n");
+ mem_heap_print(heap_1);
+
+ ut_a(mem_all_freed() == FALSE);
+
+ mem_heap_free(heap_1);
+
+ ut_a(mem_all_freed() == TRUE);
+
+ mem_print_info();
+
+ printf("-------------------------------------------\n");
+ printf("TEST 7. Test of mem_heap_fast_create \n");
+
+ heap_1 = mem_heap_fast_create(1024, block);
+
+ buf_1 = mem_heap_alloc(heap_1, 7);
+
+ ut_memcpy(buf_1, "Pascal", 7);
+
+ for (i = 0; i < 1000; i++) {
+ buf_1 = mem_heap_alloc(heap_1, 6);
+ ut_memcpy(buf_1, "Cobol", 6);
+ }
+
+ for (i = 0; i < 1000; i++) {
+ mem_heap_free_top(heap_1, 6);
+ }
+
+ ut_a(mem_all_freed() == FALSE);
+
+ mem_heap_free(heap_1);
+
+ ut_a(mem_all_freed() == TRUE);
+
+ mem_print_info();
+
+ printf("-------------------------------------------\n");
+ printf("TEST 8. Test of heap top freeing \n");
+
+ heap_1 = mem_heap_fast_create(1024, block);
+
+ top_1 = mem_heap_get_heap_top(heap_1);
+
+ buf_1 = mem_heap_alloc(heap_1, 7);
+
+ ut_memcpy(buf_1, "Pascal", 7);
+
+ for (i = 0; i < 500; i++) {
+ buf_1 = mem_heap_alloc(heap_1, 6);
+ ut_memcpy(buf_1, "Cobol", 6);
+ }
+
+ top_2 = mem_heap_get_heap_top(heap_1);
+
+ for (i = 0; i < 500; i++) {
+ buf_1 = mem_heap_alloc(heap_1, 6);
+ ut_memcpy(buf_1, "Cobol", 6);
+ }
+
+ mem_heap_free_heap_top(heap_1, top_2);
+
+ mem_heap_free_heap_top(heap_1, top_1);
+
+ ut_a(mem_all_freed() == FALSE);
+
+ for (i = 0; i < 500; i++) {
+ buf_1 = mem_heap_alloc(heap_1, 6);
+ ut_memcpy(buf_1, "Cobol", 6);
+
+ }
+
+ mem_heap_empty(heap_1);
+
+ for (i = 0; i < 500; i++) {
+ buf_1 = mem_heap_alloc(heap_1, 6);
+ ut_memcpy(buf_1, "Cobol", 6);
+
+ }
+
+ mem_heap_free(heap_1);
+
+ ut_a(mem_all_freed() == TRUE);
+
+ mem_print_info();
+
+}
+#endif /* UNIV_DEBUG */
+
+/****************************************************************
+Allocation speed test. */
+
+void
+test2(void)
+/*=======*/
+{
+ mem_heap_t* heap;
+ ulint tm, oldtm;
+ ulint i;
+ byte* buf;
+ byte block[512];
+
+ printf("-------------------------------------------\n");
+ printf("TEST B1. Test of speed \n");
+
+ oldtm = ut_clock();
+
+ for (i = 0; i < 10000 * UNIV_DBC * UNIV_DBC; i++) {
+ heap = mem_heap_create(500);
+ mem_heap_free(heap);
+ }
+
+ tm = ut_clock();
+
+ printf("Time for %ld heap create-free pairs %ld millisecs.\n",
+ i, tm - oldtm);
+
+
+ oldtm = ut_clock();
+
+ for (i = 0; i < 10000 * UNIV_DBC * UNIV_DBC; i++) {
+ heap = mem_heap_fast_create(512, block);
+ mem_heap_free(heap);
+ }
+
+ tm = ut_clock();
+
+ printf("Time for %ld heap fast-create-free pairs %ld millisecs.\n",
+ i, tm - oldtm);
+
+
+ heap = mem_heap_create(500);
+
+ oldtm = ut_clock();
+
+ for (i = 0; i < 10000 * UNIV_DBC * UNIV_DBC; i++) {
+ buf = mem_heap_alloc(heap, 50);
+ mem_heap_free_top(heap, 50);
+ }
+
+ tm = ut_clock();
+
+ printf("Time for %ld heap alloc-free-top pairs %ld millisecs.\n",
+ i, tm - oldtm);
+
+ mem_heap_free(heap);
+}
+
+
+void
+main(void)
+{
+ sync_init();
+ mem_init(2500000);
+
+ #ifdef UNIV_DEBUG
+
+ test1();
+
+ #endif
+
+ test2();
+
+ ut_ad(sync_all_freed());
+
+ ut_ad(mem_all_freed());
+
+ printf("TEST SUCCESSFULLY COMPLETED!\n");
+}