mirror of
https://github.com/MariaDB/server.git
synced 2026-05-16 11:57:38 +02:00
move to storage/innobase
This commit is contained in:
parent
1645930d0b
commit
6d06fbbd1d
360 changed files with 0 additions and 0 deletions
1050
storage/innobase/mem/mem0dbg.cc
Normal file
1050
storage/innobase/mem/mem0dbg.cc
Normal file
File diff suppressed because it is too large
Load diff
583
storage/innobase/mem/mem0mem.cc
Normal file
583
storage/innobase/mem/mem0mem.cc
Normal file
|
|
@ -0,0 +1,583 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1994, 2011, Oracle and/or its affiliates. All Rights Reserved.
|
||||
|
||||
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; version 2 of the License.
|
||||
|
||||
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.,
|
||||
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
/********************************************************************//**
|
||||
@file mem/mem0mem.cc
|
||||
The memory management
|
||||
|
||||
Created 6/9/1994 Heikki Tuuri
|
||||
*************************************************************************/
|
||||
|
||||
#include "mem0mem.h"
|
||||
#ifdef UNIV_NONINL
|
||||
#include "mem0mem.ic"
|
||||
#endif
|
||||
|
||||
#include "buf0buf.h"
|
||||
#include "srv0srv.h"
|
||||
#include "mem0dbg.cc"
|
||||
#include <stdarg.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. */
|
||||
|
||||
#ifdef MEM_PERIODIC_CHECK
|
||||
|
||||
ibool mem_block_list_inited;
|
||||
/* List of all mem blocks allocated; protected by the mem_comm_pool mutex */
|
||||
UT_LIST_BASE_NODE_T(mem_block_t) mem_block_list;
|
||||
|
||||
#endif
|
||||
|
||||
/**********************************************************************//**
|
||||
Duplicates a NUL-terminated string, allocated from a memory heap.
|
||||
@return own: a copy of the string */
|
||||
UNIV_INTERN
|
||||
char*
|
||||
mem_heap_strdup(
|
||||
/*============*/
|
||||
mem_heap_t* heap, /*!< in: memory heap where string is allocated */
|
||||
const char* str) /*!< in: string to be copied */
|
||||
{
|
||||
return(static_cast<char*>(mem_heap_dup(heap, str, strlen(str) + 1)));
|
||||
}
|
||||
|
||||
/**********************************************************************//**
|
||||
Duplicate a block of data, allocated from a memory heap.
|
||||
@return own: a copy of the data */
|
||||
UNIV_INTERN
|
||||
void*
|
||||
mem_heap_dup(
|
||||
/*=========*/
|
||||
mem_heap_t* heap, /*!< in: memory heap where copy is allocated */
|
||||
const void* data, /*!< in: data to be copied */
|
||||
ulint len) /*!< in: length of data, in bytes */
|
||||
{
|
||||
return(memcpy(mem_heap_alloc(heap, len), data, len));
|
||||
}
|
||||
|
||||
/**********************************************************************//**
|
||||
Concatenate two strings and return the result, using a memory heap.
|
||||
@return own: the result */
|
||||
UNIV_INTERN
|
||||
char*
|
||||
mem_heap_strcat(
|
||||
/*============*/
|
||||
mem_heap_t* heap, /*!< in: memory heap where string is allocated */
|
||||
const char* s1, /*!< in: string 1 */
|
||||
const char* s2) /*!< in: string 2 */
|
||||
{
|
||||
char* s;
|
||||
ulint s1_len = strlen(s1);
|
||||
ulint s2_len = strlen(s2);
|
||||
|
||||
s = static_cast<char*>(mem_heap_alloc(heap, s1_len + s2_len + 1));
|
||||
|
||||
memcpy(s, s1, s1_len);
|
||||
memcpy(s + s1_len, s2, s2_len);
|
||||
|
||||
s[s1_len + s2_len] = '\0';
|
||||
|
||||
return(s);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************//**
|
||||
Helper function for mem_heap_printf.
|
||||
@return length of formatted string, including terminating NUL */
|
||||
static
|
||||
ulint
|
||||
mem_heap_printf_low(
|
||||
/*================*/
|
||||
char* buf, /*!< in/out: buffer to store formatted string
|
||||
in, or NULL to just calculate length */
|
||||
const char* format, /*!< in: format string */
|
||||
va_list ap) /*!< in: arguments */
|
||||
{
|
||||
ulint len = 0;
|
||||
|
||||
while (*format) {
|
||||
|
||||
/* Does this format specifier have the 'l' length modifier. */
|
||||
ibool is_long = FALSE;
|
||||
|
||||
/* Length of one parameter. */
|
||||
size_t plen;
|
||||
|
||||
if (*format++ != '%') {
|
||||
/* Non-format character. */
|
||||
|
||||
len++;
|
||||
|
||||
if (buf) {
|
||||
*buf++ = *(format - 1);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*format == 'l') {
|
||||
is_long = TRUE;
|
||||
format++;
|
||||
}
|
||||
|
||||
switch (*format++) {
|
||||
case 's':
|
||||
/* string */
|
||||
{
|
||||
char* s = va_arg(ap, char*);
|
||||
|
||||
/* "%ls" is a non-sensical format specifier. */
|
||||
ut_a(!is_long);
|
||||
|
||||
plen = strlen(s);
|
||||
len += plen;
|
||||
|
||||
if (buf) {
|
||||
memcpy(buf, s, plen);
|
||||
buf += plen;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
/* unsigned int */
|
||||
{
|
||||
char tmp[32];
|
||||
unsigned long val;
|
||||
|
||||
/* We only support 'long' values for now. */
|
||||
ut_a(is_long);
|
||||
|
||||
val = va_arg(ap, unsigned long);
|
||||
|
||||
plen = sprintf(tmp, "%lu", val);
|
||||
len += plen;
|
||||
|
||||
if (buf) {
|
||||
memcpy(buf, tmp, plen);
|
||||
buf += plen;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '%':
|
||||
|
||||
/* "%l%" is a non-sensical format specifier. */
|
||||
ut_a(!is_long);
|
||||
|
||||
len++;
|
||||
|
||||
if (buf) {
|
||||
*buf++ = '%';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ut_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* For the NUL character. */
|
||||
len++;
|
||||
|
||||
if (buf) {
|
||||
*buf = '\0';
|
||||
}
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
/****************************************************************//**
|
||||
A simple sprintf replacement that dynamically allocates the space for the
|
||||
formatted string from the given heap. This supports a very limited set of
|
||||
the printf syntax: types 's' and 'u' and length modifier 'l' (which is
|
||||
required for the 'u' type).
|
||||
@return heap-allocated formatted string */
|
||||
UNIV_INTERN
|
||||
char*
|
||||
mem_heap_printf(
|
||||
/*============*/
|
||||
mem_heap_t* heap, /*!< in: memory heap */
|
||||
const char* format, /*!< in: format string */
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
char* str;
|
||||
ulint len;
|
||||
|
||||
/* Calculate length of string */
|
||||
len = 0;
|
||||
va_start(ap, format);
|
||||
len = mem_heap_printf_low(NULL, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
/* Now create it for real. */
|
||||
str = static_cast<char*>(mem_heap_alloc(heap, len));
|
||||
va_start(ap, format);
|
||||
mem_heap_printf_low(str, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return(str);
|
||||
}
|
||||
|
||||
/***************************************************************//**
|
||||
Creates a memory heap block where data can be allocated.
|
||||
@return own: memory heap block, NULL if did not succeed (only possible
|
||||
for MEM_HEAP_BTR_SEARCH type heaps) */
|
||||
UNIV_INTERN
|
||||
mem_block_t*
|
||||
mem_heap_create_block_func(
|
||||
/*=======================*/
|
||||
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 */
|
||||
#ifdef UNIV_DEBUG
|
||||
const char* file_name,/*!< in: file name where created */
|
||||
ulint line, /*!< in: line where created */
|
||||
#endif /* UNIV_DEBUG */
|
||||
ulint type) /*!< in: type of heap: MEM_HEAP_DYNAMIC or
|
||||
MEM_HEAP_BUFFER */
|
||||
{
|
||||
#ifndef UNIV_HOTBACKUP
|
||||
buf_block_t* buf_block = NULL;
|
||||
#endif /* !UNIV_HOTBACKUP */
|
||||
mem_block_t* block;
|
||||
ulint len;
|
||||
|
||||
ut_ad((type == MEM_HEAP_DYNAMIC) || (type == MEM_HEAP_BUFFER)
|
||||
|| (type == MEM_HEAP_BUFFER + MEM_HEAP_BTR_SEARCH));
|
||||
|
||||
if (heap && heap->magic_n != MEM_BLOCK_MAGIC_N) {
|
||||
mem_analyze_corruption(heap);
|
||||
}
|
||||
|
||||
/* In dynamic allocation, calculate the size: block header + data. */
|
||||
len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
|
||||
|
||||
#ifndef UNIV_HOTBACKUP
|
||||
if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) {
|
||||
|
||||
ut_ad(type == MEM_HEAP_DYNAMIC || n <= MEM_MAX_ALLOC_IN_BUF);
|
||||
|
||||
block = static_cast<mem_block_t*>(
|
||||
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 */
|
||||
|
||||
buf_block = static_cast<buf_block_t*>(heap->free_block);
|
||||
heap->free_block = NULL;
|
||||
|
||||
if (UNIV_UNLIKELY(!buf_block)) {
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
} else {
|
||||
buf_block = buf_block_alloc(NULL);
|
||||
}
|
||||
|
||||
block = (mem_block_t*) buf_block->frame;
|
||||
}
|
||||
|
||||
if(!block) {
|
||||
ib_logf(IB_LOG_LEVEL_FATAL,
|
||||
" InnoDB: Unable to allocate memory of size %lu.\n",
|
||||
len);
|
||||
}
|
||||
block->buf_block = buf_block;
|
||||
block->free_block = NULL;
|
||||
#else /* !UNIV_HOTBACKUP */
|
||||
len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
|
||||
block = ut_malloc(len);
|
||||
ut_ad(block);
|
||||
#endif /* !UNIV_HOTBACKUP */
|
||||
|
||||
block->magic_n = MEM_BLOCK_MAGIC_N;
|
||||
ut_d(ut_strlcpy_rev(block->file_name, file_name,
|
||||
sizeof(block->file_name)));
|
||||
ut_d(block->line = line);
|
||||
|
||||
#ifdef MEM_PERIODIC_CHECK
|
||||
mutex_enter(&(mem_comm_pool->mutex));
|
||||
|
||||
if (!mem_block_list_inited) {
|
||||
mem_block_list_inited = TRUE;
|
||||
UT_LIST_INIT(mem_block_list);
|
||||
}
|
||||
|
||||
UT_LIST_ADD_LAST(mem_block_list, mem_block_list, block);
|
||||
|
||||
mutex_exit(&(mem_comm_pool->mutex));
|
||||
#endif
|
||||
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);
|
||||
|
||||
if (UNIV_UNLIKELY(heap == NULL)) {
|
||||
/* This is the first block of the heap. The field
|
||||
total_size should be initialized here */
|
||||
block->total_size = len;
|
||||
} else {
|
||||
/* Not the first allocation for the heap. This block's
|
||||
total_length field should be set to undefined. */
|
||||
ut_d(block->total_size = ULINT_UNDEFINED);
|
||||
UNIV_MEM_INVALID(&block->total_size,
|
||||
sizeof block->total_size);
|
||||
|
||||
heap->total_size += len;
|
||||
}
|
||||
|
||||
ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len);
|
||||
|
||||
return(block);
|
||||
}
|
||||
|
||||
/***************************************************************//**
|
||||
Adds a new block to a memory heap.
|
||||
@return created block, NULL if did not succeed (only possible for
|
||||
MEM_HEAP_BTR_SEARCH type heaps) */
|
||||
UNIV_INTERN
|
||||
mem_block_t*
|
||||
mem_heap_add_block(
|
||||
/*===============*/
|
||||
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) {
|
||||
/* From the buffer pool we allocate buffer frames */
|
||||
ut_a(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, heap->type,
|
||||
heap->file_name, heap->line);
|
||||
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. */
|
||||
UNIV_INTERN
|
||||
void
|
||||
mem_heap_block_free(
|
||||
/*================*/
|
||||
mem_heap_t* heap, /*!< in: heap */
|
||||
mem_block_t* block) /*!< in: block to free */
|
||||
{
|
||||
ulint type;
|
||||
ulint len;
|
||||
#ifndef UNIV_HOTBACKUP
|
||||
buf_block_t* buf_block;
|
||||
|
||||
buf_block = static_cast<buf_block_t*>(block->buf_block);
|
||||
#endif /* !UNIV_HOTBACKUP */
|
||||
|
||||
if (block->magic_n != MEM_BLOCK_MAGIC_N) {
|
||||
mem_analyze_corruption(block);
|
||||
}
|
||||
|
||||
UT_LIST_REMOVE(list, heap->base, block);
|
||||
|
||||
#ifdef MEM_PERIODIC_CHECK
|
||||
mutex_enter(&(mem_comm_pool->mutex));
|
||||
|
||||
UT_LIST_REMOVE(mem_block_list, mem_block_list, block);
|
||||
|
||||
mutex_exit(&(mem_comm_pool->mutex));
|
||||
#endif
|
||||
|
||||
ut_ad(heap->total_size >= block->len);
|
||||
heap->total_size -= block->len;
|
||||
|
||||
type = heap->type;
|
||||
len = block->len;
|
||||
block->magic_n = MEM_FREED_BLOCK_MAGIC_N;
|
||||
|
||||
#ifndef UNIV_HOTBACKUP
|
||||
if (!srv_use_sys_malloc) {
|
||||
#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);
|
||||
#else /* UNIV_MEM_DEBUG */
|
||||
UNIV_MEM_ASSERT_AND_FREE(block, len);
|
||||
#endif /* UNIV_MEM_DEBUG */
|
||||
|
||||
}
|
||||
if (type == MEM_HEAP_DYNAMIC || len < UNIV_PAGE_SIZE / 2) {
|
||||
|
||||
ut_ad(!buf_block);
|
||||
mem_area_free(block, mem_comm_pool);
|
||||
} else {
|
||||
ut_ad(type & MEM_HEAP_BUFFER);
|
||||
|
||||
buf_block_free(buf_block);
|
||||
}
|
||||
#else /* !UNIV_HOTBACKUP */
|
||||
#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);
|
||||
#else /* UNIV_MEM_DEBUG */
|
||||
UNIV_MEM_ASSERT_AND_FREE(block, len);
|
||||
#endif /* UNIV_MEM_DEBUG */
|
||||
ut_free(block);
|
||||
#endif /* !UNIV_HOTBACKUP */
|
||||
}
|
||||
|
||||
#ifndef UNIV_HOTBACKUP
|
||||
/******************************************************************//**
|
||||
Frees the free_block field from a memory heap. */
|
||||
UNIV_INTERN
|
||||
void
|
||||
mem_heap_free_block_free(
|
||||
/*=====================*/
|
||||
mem_heap_t* heap) /*!< in: heap */
|
||||
{
|
||||
if (UNIV_LIKELY_NULL(heap->free_block)) {
|
||||
|
||||
buf_block_free(static_cast<buf_block_t*>(heap->free_block));
|
||||
|
||||
heap->free_block = NULL;
|
||||
}
|
||||
}
|
||||
#endif /* !UNIV_HOTBACKUP */
|
||||
|
||||
#ifdef MEM_PERIODIC_CHECK
|
||||
/******************************************************************//**
|
||||
Goes through the list of all allocated mem blocks, checks their magic
|
||||
numbers, and reports possible corruption. */
|
||||
UNIV_INTERN
|
||||
void
|
||||
mem_validate_all_blocks(void)
|
||||
/*=========================*/
|
||||
{
|
||||
mem_block_t* block;
|
||||
|
||||
mutex_enter(&(mem_comm_pool->mutex));
|
||||
|
||||
block = UT_LIST_GET_FIRST(mem_block_list);
|
||||
|
||||
while (block) {
|
||||
if (block->magic_n != MEM_BLOCK_MAGIC_N) {
|
||||
mem_analyze_corruption(block);
|
||||
}
|
||||
|
||||
block = UT_LIST_GET_NEXT(mem_block_list, block);
|
||||
}
|
||||
|
||||
mutex_exit(&(mem_comm_pool->mutex));
|
||||
}
|
||||
#endif
|
||||
727
storage/innobase/mem/mem0pool.cc
Normal file
727
storage/innobase/mem/mem0pool.cc
Normal file
|
|
@ -0,0 +1,727 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1997, 2011, Oracle and/or its affiliates. All Rights Reserved.
|
||||
|
||||
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; version 2 of the License.
|
||||
|
||||
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.,
|
||||
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
/********************************************************************//**
|
||||
@file mem/mem0pool.cc
|
||||
The lowest-level memory management
|
||||
|
||||
Created 5/12/1997 Heikki Tuuri
|
||||
*************************************************************************/
|
||||
|
||||
#include "mem0pool.h"
|
||||
#ifdef UNIV_NONINL
|
||||
#include "mem0pool.ic"
|
||||
#endif
|
||||
|
||||
#include "srv0srv.h"
|
||||
#include "sync0sync.h"
|
||||
#include "ut0mem.h"
|
||||
#include "ut0lst.h"
|
||||
#include "ut0byte.h"
|
||||
#include "mem0mem.h"
|
||||
#include "srv0start.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 and 2 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.
|
||||
|
||||
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 request memory from
|
||||
the operating system. 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. */
|
||||
|
||||
/** 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 * MEM_AREA_EXTRA_SIZE)
|
||||
|
||||
|
||||
/** 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_t{
|
||||
byte* buf; /*!< memory pool */
|
||||
ulint size; /*!< memory common pool size */
|
||||
ulint reserved; /*!< amount of currently allocated
|
||||
memory */
|
||||
ib_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 */
|
||||
UNIV_INTERN mem_pool_t* mem_comm_pool = NULL;
|
||||
|
||||
#ifdef UNIV_PFS_MUTEX
|
||||
/* Key to register mutex in mem_pool_t with performance schema */
|
||||
UNIV_INTERN mysql_pfs_key_t mem_pool_mutex_key;
|
||||
#endif /* UNIV_PFS_MUTEX */
|
||||
|
||||
/* We use this counter to check that the mem pool mutex does not leak;
|
||||
this is to track a strange assertion failure reported at
|
||||
mysql@lists.mysql.com */
|
||||
|
||||
UNIV_INTERN ulint mem_n_threads_inside = 0;
|
||||
|
||||
/********************************************************************//**
|
||||
Reserves the mem pool mutex if we are not in server shutdown. Use
|
||||
this function only in memory free functions, since only memory
|
||||
free functions are used during server shutdown. */
|
||||
UNIV_INLINE
|
||||
void
|
||||
mem_pool_mutex_enter(
|
||||
/*=================*/
|
||||
mem_pool_t* pool) /*!< in: memory pool */
|
||||
{
|
||||
if (srv_shutdown_state < SRV_SHUTDOWN_EXIT_THREADS) {
|
||||
mutex_enter(&(pool->mutex));
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Releases the mem pool mutex if we are not in server shutdown. As
|
||||
its corresponding mem_pool_mutex_enter() function, use it only
|
||||
in memory free functions */
|
||||
UNIV_INLINE
|
||||
void
|
||||
mem_pool_mutex_exit(
|
||||
/*================*/
|
||||
mem_pool_t* pool) /*!< in: memory pool */
|
||||
{
|
||||
if (srv_shutdown_state < SRV_SHUTDOWN_EXIT_THREADS) {
|
||||
mutex_exit(&(pool->mutex));
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Returns memory area size.
|
||||
@return size */
|
||||
UNIV_INLINE
|
||||
ulint
|
||||
mem_area_get_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.
|
||||
@return TRUE if free */
|
||||
UNIV_INLINE
|
||||
ibool
|
||||
mem_area_get_free(
|
||||
/*==============*/
|
||||
mem_area_t* area) /*!< in: area */
|
||||
{
|
||||
#if TRUE != MEM_AREA_FREE
|
||||
# error "TRUE != MEM_AREA_FREE"
|
||||
#endif
|
||||
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 */
|
||||
{
|
||||
#if TRUE != MEM_AREA_FREE
|
||||
# error "TRUE != MEM_AREA_FREE"
|
||||
#endif
|
||||
area->size_and_free = (area->size_and_free & ~MEM_AREA_FREE)
|
||||
| free;
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
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 = static_cast<mem_pool_t*>(ut_malloc(sizeof(mem_pool_t)));
|
||||
|
||||
pool->buf = static_cast<byte*>(ut_malloc_low(size, TRUE));
|
||||
pool->size = size;
|
||||
|
||||
mutex_create(mem_pool_mutex_key, &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 a memory pool. */
|
||||
UNIV_INTERN
|
||||
void
|
||||
mem_pool_free(
|
||||
/*==========*/
|
||||
mem_pool_t* pool) /*!< in, own: memory pool */
|
||||
{
|
||||
ut_free(pool->buf);
|
||||
ut_free(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);
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
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
|
||||
mysql@lists.mysql.com 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)));
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Gets the buddy of an area, if it exists in pool.
|
||||
@return the buddy, NULL if no buddy in pool */
|
||||
UNIV_INLINE
|
||||
mem_area_t*
|
||||
mem_area_get_buddy(
|
||||
/*===============*/
|
||||
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. */
|
||||
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));
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Validates a memory pool.
|
||||
@return TRUE if ok */
|
||||
UNIV_INTERN
|
||||
ibool
|
||||
mem_pool_validate(
|
||||
/*==============*/
|
||||
mem_pool_t* pool) /*!< in: memory pool */
|
||||
{
|
||||
mem_area_t* area;
|
||||
mem_area_t* buddy;
|
||||
ulint free;
|
||||
ulint i;
|
||||
|
||||
mem_pool_mutex_enter(pool);
|
||||
|
||||
free = 0;
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
|
||||
UT_LIST_CHECK(free_list, mem_area_t, pool->free_list[i]);
|
||||
|
||||
for (area = UT_LIST_GET_FIRST(pool->free_list[i]);
|
||||
area != 0;
|
||||
area = UT_LIST_GET_NEXT(free_list, area)) {
|
||||
|
||||
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)));
|
||||
|
||||
free += ut_2_exp(i);
|
||||
}
|
||||
}
|
||||
|
||||
ut_a(free + pool->reserved == pool->size);
|
||||
|
||||
mem_pool_mutex_exit(pool);
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Prints info of a memory pool. */
|
||||
UNIV_INTERN
|
||||
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",
|
||||
(ulong) UT_LIST_GET_LEN(pool->free_list[i]),
|
||||
(ulong) ut_2_exp(i));
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(outfile, "Pool size %lu, reserved %lu.\n", (ulong) pool->size,
|
||||
(ulong) pool->reserved);
|
||||
mutex_exit(&(pool->mutex));
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Returns the amount of reserved memory.
|
||||
@return reserved memory in bytes */
|
||||
UNIV_INTERN
|
||||
ulint
|
||||
mem_pool_get_reserved(
|
||||
/*==================*/
|
||||
mem_pool_t* pool) /*!< in: memory pool */
|
||||
{
|
||||
ulint reserved;
|
||||
|
||||
mutex_enter(&(pool->mutex));
|
||||
|
||||
reserved = pool->reserved;
|
||||
|
||||
mutex_exit(&(pool->mutex));
|
||||
|
||||
return(reserved);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue