mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 12:56:14 +01:00 
			
		
		
		
	Let us use implement a simple fixed-size allocator for the adaptive hash index, insted of complicating mem_heap_t or mem_block_info_t. MEM_HEAP_BTR_SEARCH: Remove. mem_block_info_t::free_block(), mem_heap_free_block_free(): Remove. mem_heap_free_top(), mem_heap_get_top(): Remove. btr_sea::partition::spare: Replaces mem_block_info_t::free_block. This keeps one spare block per adaptive hash index partition, to process an insert. We must not wait for buf_pool.mutex while holding any btr_sea::partition::latch. That is why we cache one block for future allocations. This is protected by a new btr_sea::partition::blocks_mutex in order to relieve pressure on btr_sea::partition::latch. btr_sea::partition::prepare_insert(): Replaces btr_search_check_free_space_in_heap(). btr_sea::partition::erase(): Replaces ha_search_and_delete_if_found(). btr_sea::partition::cleanup_after_erase(): Replaces the most part of ha_delete_hash_node(). Unlike the previous implementation, we will retain a spare block for prepare_insert(). This should reduce some contention on buf_pool.mutex. btr_search.n_parts: Replaces btr_ahi_parts. btr_search.enabled: Replaces btr_search_enabled. This must hold whenever buf_block_t::index is set while a thread is holding a btr_sea::partition::latch. dict_index_t::search_info: Remove pointer indirection, and use Atomic_relaxed or Atomic_counter for most fields. btr_search_guess_on_hash(): Let the caller ensure that latch_mode is BTR_MODIFY_LEAF or BTR_SEARCH_LEAF. Release btr_sea::partition::latch before buffer-fixing the block. The page latch that we already acquired is preventing buffer pool eviction. We must validate both block->index and block->page.state while holding part.latch in order to avoid race conditions with buffer page relocation or buf_pool_t::resize(). btr_search_check_guess(): Remove the constant parameter can_only_compare_to_cursor_rec=false. ahi_node: Replaces ha_node_t. This has been tested by running the regression test suite with the adaptive hash index enabled: ./mtr --mysqld=--loose-innodb-adaptive-hash-index=ON Reviewed by: Vladislav Lesin
		
			
				
	
	
		
			401 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			401 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*****************************************************************************
 | 
						|
 | 
						|
Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved.
 | 
						|
Copyright (c) 2017, 2021, MariaDB Corporation.
 | 
						|
 | 
						|
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, Fifth Floor, Boston, MA 02110-1335 USA
 | 
						|
 | 
						|
*****************************************************************************/
 | 
						|
 | 
						|
/********************************************************************//**
 | 
						|
@file mem/mem0mem.cc
 | 
						|
The memory management
 | 
						|
 | 
						|
Created 6/9/1994 Heikki Tuuri
 | 
						|
*************************************************************************/
 | 
						|
 | 
						|
#include "mem0mem.h"
 | 
						|
#include "buf0buf.h"
 | 
						|
#include "buf0lru.h"
 | 
						|
#include "srv0srv.h"
 | 
						|
#include <stdarg.h>
 | 
						|
 | 
						|
/**********************************************************************//**
 | 
						|
Concatenate two strings and return the result, using a memory heap.
 | 
						|
@return own: the result */
 | 
						|
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 = size_t(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 */
 | 
						|
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);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef UNIV_DEBUG
 | 
						|
/** Validates the contents of a memory heap.
 | 
						|
Checks a memory heap for consistency, prints the contents if any error
 | 
						|
is detected. A fatal error is logged if an error is detected.
 | 
						|
@param[in]	heap	Memory heap to validate. */
 | 
						|
void
 | 
						|
mem_heap_validate(
 | 
						|
	const mem_heap_t*	heap)
 | 
						|
{
 | 
						|
	ulint	size = 0;
 | 
						|
 | 
						|
	for (const mem_block_t* block = heap;
 | 
						|
		block != NULL;
 | 
						|
		block = UT_LIST_GET_NEXT(list, block)) {
 | 
						|
 | 
						|
		switch (block->type) {
 | 
						|
		case MEM_HEAP_DYNAMIC:
 | 
						|
			break;
 | 
						|
		case MEM_HEAP_BUFFER:
 | 
						|
			ut_ad(block->len <= srv_page_size);
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			ut_error;
 | 
						|
		}
 | 
						|
 | 
						|
		size += block->len;
 | 
						|
	}
 | 
						|
 | 
						|
	ut_ad(size == heap->total_size);
 | 
						|
}
 | 
						|
 | 
						|
/** Copy the tail of a string.
 | 
						|
@param[in,out]	dst	destination buffer
 | 
						|
@param[in]	src	string whose tail to copy
 | 
						|
@param[in]	size	size of dst buffer, in bytes, including NUL terminator
 | 
						|
@return strlen(src) */
 | 
						|
static void ut_strlcpy_rev(char* dst, const char* src, ulint size)
 | 
						|
{
 | 
						|
	size_t src_size = strlen(src), n = std::min(src_size, size - 1);
 | 
						|
	memcpy(dst, src + src_size - n, n + 1);
 | 
						|
}
 | 
						|
#endif /* UNIV_DEBUG */
 | 
						|
 | 
						|
/***************************************************************//**
 | 
						|
Creates a memory heap block where data can be allocated.
 | 
						|
@return own: memory heap block */
 | 
						|
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 */
 | 
						|
	unsigned	line,	/*!< in: line where created */
 | 
						|
#endif /* UNIV_DEBUG */
 | 
						|
	ulint		type)	/*!< in: type of heap: MEM_HEAP_DYNAMIC or
 | 
						|
				MEM_HEAP_BUFFER */
 | 
						|
{
 | 
						|
	buf_block_t*	buf_block;
 | 
						|
	mem_block_t*	block;
 | 
						|
	ulint		len;
 | 
						|
 | 
						|
	ut_ad(type == MEM_HEAP_DYNAMIC || type == MEM_HEAP_BUFFER);
 | 
						|
 | 
						|
	if (heap != NULL) {
 | 
						|
		ut_d(mem_heap_validate(heap));
 | 
						|
	}
 | 
						|
 | 
						|
	/* In dynamic allocation, calculate the size: block header + data. */
 | 
						|
	len = MEM_BLOCK_HEADER_SIZE + MEM_SPACE_NEEDED(n);
 | 
						|
 | 
						|
	if (type == MEM_HEAP_DYNAMIC || len < srv_page_size / 2) {
 | 
						|
 | 
						|
		ut_ad(type == MEM_HEAP_DYNAMIC || n <= MEM_MAX_ALLOC_IN_BUF);
 | 
						|
 | 
						|
		block = static_cast<mem_block_t*>(ut_malloc_nokey(len));
 | 
						|
		buf_block = nullptr;
 | 
						|
	} else {
 | 
						|
		len = srv_page_size;
 | 
						|
 | 
						|
		buf_block = buf_block_alloc();
 | 
						|
 | 
						|
		block = (mem_block_t*) buf_block->page.frame;
 | 
						|
	}
 | 
						|
 | 
						|
	if (block == NULL) {
 | 
						|
		ib::fatal() << "Unable to allocate memory of size "
 | 
						|
			<< len << ".";
 | 
						|
	}
 | 
						|
 | 
						|
	block->buf_block = buf_block;
 | 
						|
 | 
						|
	ut_d(ut_strlcpy_rev(block->file_name, file_name,
 | 
						|
			    sizeof(block->file_name)));
 | 
						|
	ut_d(block->line = line);
 | 
						|
 | 
						|
	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);
 | 
						|
		MEM_UNDEFINED(&block->total_size, sizeof block->total_size);
 | 
						|
 | 
						|
		heap->total_size += len;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Poison all available memory. Individual chunks will be unpoisoned on
 | 
						|
	every mem_heap_alloc() call. */
 | 
						|
	compile_time_assert(MEM_BLOCK_HEADER_SIZE >= sizeof *block);
 | 
						|
	MEM_NOACCESS(block + 1, len - sizeof *block);
 | 
						|
 | 
						|
	ut_ad((ulint)MEM_BLOCK_HEADER_SIZE < len);
 | 
						|
 | 
						|
	return(block);
 | 
						|
}
 | 
						|
 | 
						|
/***************************************************************//**
 | 
						|
Adds a new block to a memory heap.
 | 
						|
@return created block */
 | 
						|
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;
 | 
						|
 | 
						|
	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(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;
 | 
						|
 | 
						|
	UT_LIST_REMOVE(heap->base, block);
 | 
						|
 | 
						|
	ut_ad(heap->total_size >= block->len);
 | 
						|
	heap->total_size -= block->len;
 | 
						|
 | 
						|
	type = heap->type;
 | 
						|
	len = block->len;
 | 
						|
 | 
						|
	if (type == MEM_HEAP_DYNAMIC || len < srv_page_size / 2) {
 | 
						|
		ut_ad(!block->buf_block);
 | 
						|
		ut_free(block);
 | 
						|
	} else {
 | 
						|
		ut_ad(type & MEM_HEAP_BUFFER);
 | 
						|
		buf_block_free(block->buf_block);
 | 
						|
	}
 | 
						|
}
 |