mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
memroot improvement: fix savepoint support
savepoint support was added a year ago as get_last_memroot_block() and free_all_new_blocks(), but it didn't work and was disabled. * fix it to work * instead of freeing the memory, only mark blocks free - this feature is supposed to be used inside a loop (otherwise there is no need to free anything, end of statement will do it anyway). And freeing blocks inside a loop is a bad idea, as they'll be all malloc-ed on the next iteration again. So, don't. * fix a bug in mark_blocks_free() - it doesn't change the number of blocks
This commit is contained in:
parent
4f4c5a2ba9
commit
9f2adffcca
4 changed files with 63 additions and 40 deletions
|
@ -60,6 +60,14 @@ typedef struct st_mem_root
|
||||||
PSI_memory_key psi_key;
|
PSI_memory_key psi_key;
|
||||||
} MEM_ROOT;
|
} MEM_ROOT;
|
||||||
|
|
||||||
|
typedef struct st_mem_root_savepoint
|
||||||
|
{
|
||||||
|
MEM_ROOT *root;
|
||||||
|
USED_MEM *free;
|
||||||
|
USED_MEM *used;
|
||||||
|
unsigned short first_block_usage;
|
||||||
|
} MEM_ROOT_SAVEPOINT;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -921,8 +921,8 @@ extern void move_root(MEM_ROOT *to, MEM_ROOT *from);
|
||||||
extern void set_prealloc_root(MEM_ROOT *root, char *ptr);
|
extern void set_prealloc_root(MEM_ROOT *root, char *ptr);
|
||||||
extern void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
|
extern void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
|
||||||
size_t prealloc_size);
|
size_t prealloc_size);
|
||||||
extern USED_MEM *get_last_memroot_block(MEM_ROOT* root);
|
extern void root_make_savepoint(MEM_ROOT *root, MEM_ROOT_SAVEPOINT *sv);
|
||||||
extern void free_all_new_blocks(MEM_ROOT *root, USED_MEM *last_block);
|
extern void root_free_to_savepoint(const MEM_ROOT_SAVEPOINT *sv);
|
||||||
extern void protect_root(MEM_ROOT *root, int prot);
|
extern void protect_root(MEM_ROOT *root, int prot);
|
||||||
extern char *strdup_root(MEM_ROOT *root,const char *str);
|
extern char *strdup_root(MEM_ROOT *root,const char *str);
|
||||||
static inline char *safe_strdup_root(MEM_ROOT *root, const char *str)
|
static inline char *safe_strdup_root(MEM_ROOT *root, const char *str)
|
||||||
|
|
|
@ -266,8 +266,8 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
|
||||||
{
|
{
|
||||||
size_t get_size, block_size;
|
size_t get_size, block_size;
|
||||||
uchar* point;
|
uchar* point;
|
||||||
reg1 USED_MEM *next= 0;
|
USED_MEM *next= 0;
|
||||||
reg2 USED_MEM **prev;
|
USED_MEM **prev;
|
||||||
size_t original_length __attribute__((unused)) = length;
|
size_t original_length __attribute__((unused)) = length;
|
||||||
DBUG_ENTER("alloc_root");
|
DBUG_ENTER("alloc_root");
|
||||||
DBUG_PRINT("enter",("root: %p", mem_root));
|
DBUG_PRINT("enter",("root: %p", mem_root));
|
||||||
|
@ -425,10 +425,10 @@ void *multi_alloc_root(MEM_ROOT *root, ...)
|
||||||
#if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG))
|
#if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG))
|
||||||
/** Mark all data in blocks free for reusage */
|
/** Mark all data in blocks free for reusage */
|
||||||
|
|
||||||
static inline void mark_blocks_free(MEM_ROOT* root)
|
static void mark_blocks_free(MEM_ROOT* root)
|
||||||
{
|
{
|
||||||
reg1 USED_MEM *next;
|
USED_MEM *next;
|
||||||
reg2 USED_MEM **last;
|
USED_MEM **last;
|
||||||
|
|
||||||
/* iterate through (partially) free blocks, mark them free */
|
/* iterate through (partially) free blocks, mark them free */
|
||||||
last= &root->free;
|
last= &root->free;
|
||||||
|
@ -451,7 +451,6 @@ static inline void mark_blocks_free(MEM_ROOT* root)
|
||||||
/* Now everything is set; Indicate that nothing is used anymore */
|
/* Now everything is set; Indicate that nothing is used anymore */
|
||||||
root->used= 0;
|
root->used= 0;
|
||||||
root->first_block_usage= 0;
|
root->first_block_usage= 0;
|
||||||
root->block_num= 4;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -477,7 +476,7 @@ static inline void mark_blocks_free(MEM_ROOT* root)
|
||||||
|
|
||||||
void free_root(MEM_ROOT *root, myf MyFlags)
|
void free_root(MEM_ROOT *root, myf MyFlags)
|
||||||
{
|
{
|
||||||
reg1 USED_MEM *next,*old;
|
USED_MEM *next,*old;
|
||||||
DBUG_ENTER("free_root");
|
DBUG_ENTER("free_root");
|
||||||
DBUG_PRINT("enter",("root: %p flags: %lu", root, MyFlags));
|
DBUG_PRINT("enter",("root: %p flags: %lu", root, MyFlags));
|
||||||
|
|
||||||
|
@ -567,45 +566,61 @@ void move_root(MEM_ROOT *to, MEM_ROOT *from)
|
||||||
from->used= 0;
|
from->used= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Remember last MEM_ROOT block.
|
Prepare MEM_ROOT to a later truncation. Everything allocated after
|
||||||
|
that point can be freed while keeping earlier allocations intact.
|
||||||
|
|
||||||
This allows one to free all new allocated blocks.
|
For this to work we cannot allow new allocations in partially filled blocks,
|
||||||
|
so remove all non-empty blocks from the memroot. For simplicity, let's
|
||||||
|
also remove all used blocks.
|
||||||
*/
|
*/
|
||||||
|
void root_make_savepoint(MEM_ROOT *root, MEM_ROOT_SAVEPOINT *sv)
|
||||||
USED_MEM *get_last_memroot_block(MEM_ROOT* root)
|
|
||||||
{
|
{
|
||||||
return root->used ? root->used : root->pre_alloc;
|
USED_MEM **prev= &root->free, *block= *prev;
|
||||||
|
for ( ; block; prev= &block->next, block= *prev)
|
||||||
|
if (block->left < block->size - ALIGN_SIZE(sizeof(USED_MEM)))
|
||||||
|
break;
|
||||||
|
sv->root= root;
|
||||||
|
sv->free= block;
|
||||||
|
sv->used= root->used;
|
||||||
|
sv->first_block_usage= root->first_block_usage;
|
||||||
|
*prev= 0;
|
||||||
|
root->used= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Free all newly allocated blocks
|
Restore MEM_ROOT to the state before the savepoint was made.
|
||||||
|
|
||||||
|
Restore old free and used lists.
|
||||||
|
Mark all new (after savepoint) used and partially used blocks free
|
||||||
|
and put them into the free list.
|
||||||
*/
|
*/
|
||||||
|
void root_free_to_savepoint(const MEM_ROOT_SAVEPOINT *sv)
|
||||||
void free_all_new_blocks(MEM_ROOT *root, USED_MEM *last_block)
|
|
||||||
{
|
{
|
||||||
USED_MEM *old, *next;
|
MEM_ROOT *root= sv->root;
|
||||||
if (!root->used)
|
USED_MEM **prev= &root->free, *block= *prev;
|
||||||
return; /* Nothing allocated */
|
|
||||||
return;
|
|
||||||
/*
|
|
||||||
Free everying allocated up to, but not including, last_block.
|
|
||||||
However do not go past pre_alloc as we do not want to free
|
|
||||||
that one. This should not be a problem as in almost all normal
|
|
||||||
usage pre_alloc is last in the list.
|
|
||||||
*/
|
|
||||||
|
|
||||||
for (next= root->used ;
|
/* iterate through (partially) free blocks, mark them free */
|
||||||
next && next != last_block && next != root->pre_alloc ; )
|
for ( ; block; prev= &block->next, block= *prev)
|
||||||
{
|
{
|
||||||
old= next; next= next->next;
|
block->left= block->size - ALIGN_SIZE(sizeof(USED_MEM));
|
||||||
root_free(root, old, old->size);
|
TRASH_MEM(block);
|
||||||
}
|
}
|
||||||
root->used= next;
|
|
||||||
root->block_num= 4;
|
/* Combine the free and the used list */
|
||||||
root->first_block_usage= 0;
|
*prev= block=root->used;
|
||||||
|
|
||||||
|
/* now go through the used blocks and mark them free */
|
||||||
|
for ( ; block; prev= &block->next, block= *prev)
|
||||||
|
{
|
||||||
|
block->left= block->size - ALIGN_SIZE(sizeof(USED_MEM));
|
||||||
|
TRASH_MEM(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* restore free and used lists from savepoint */
|
||||||
|
*prev= sv->free;
|
||||||
|
root->used= sv->used;
|
||||||
|
root->first_block_usage= prev == &root->free ? sv->first_block_usage : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -615,7 +630,7 @@ void free_all_new_blocks(MEM_ROOT *root, USED_MEM *last_block)
|
||||||
#if defined(HAVE_MMAP) && defined(HAVE_MPROTECT) && defined(MAP_ANONYMOUS)
|
#if defined(HAVE_MMAP) && defined(HAVE_MPROTECT) && defined(MAP_ANONYMOUS)
|
||||||
void protect_root(MEM_ROOT *root, int prot)
|
void protect_root(MEM_ROOT *root, int prot)
|
||||||
{
|
{
|
||||||
reg1 USED_MEM *next,*old;
|
USED_MEM *next,*old;
|
||||||
DBUG_ENTER("protect_root");
|
DBUG_ENTER("protect_root");
|
||||||
DBUG_PRINT("enter",("root: %p prot: %d", root, prot));
|
DBUG_PRINT("enter",("root: %p prot: %d", root, prot));
|
||||||
|
|
||||||
|
|
|
@ -982,7 +982,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||||
{
|
{
|
||||||
TABLE *tab= table->table;
|
TABLE *tab= table->table;
|
||||||
Field **field_ptr= tab->field;
|
Field **field_ptr= tab->field;
|
||||||
USED_MEM *memroot_block;
|
MEM_ROOT_SAVEPOINT memroot_sv;
|
||||||
|
|
||||||
if (!lex->column_list)
|
if (!lex->column_list)
|
||||||
{
|
{
|
||||||
|
@ -1084,7 +1084,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||||
}
|
}
|
||||||
/* Ensure that number of records are updated */
|
/* Ensure that number of records are updated */
|
||||||
tab->file->info(HA_STATUS_VARIABLE);
|
tab->file->info(HA_STATUS_VARIABLE);
|
||||||
memroot_block= get_last_memroot_block(thd->mem_root);
|
root_make_savepoint(thd->mem_root, &memroot_sv);
|
||||||
if (!(compl_result_code=
|
if (!(compl_result_code=
|
||||||
alloc_statistics_for_table(thd, tab,
|
alloc_statistics_for_table(thd, tab,
|
||||||
&tab->has_value_set)) &&
|
&tab->has_value_set)) &&
|
||||||
|
@ -1092,7 +1092,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
||||||
collect_statistics_for_table(thd, tab)))
|
collect_statistics_for_table(thd, tab)))
|
||||||
compl_result_code= update_statistics_for_table(thd, tab);
|
compl_result_code= update_statistics_for_table(thd, tab);
|
||||||
free_statistics_for_table(tab);
|
free_statistics_for_table(tab);
|
||||||
free_all_new_blocks(thd->mem_root, memroot_block);
|
root_free_to_savepoint(&memroot_sv);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
compl_result_code= HA_ADMIN_FAILED;
|
compl_result_code= HA_ADMIN_FAILED;
|
||||||
|
|
Loading…
Reference in a new issue