Skip to content

Commit 9f2adff

Browse files
committed
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
1 parent 4f4c5a2 commit 9f2adff

File tree

4 files changed

+63
-40
lines changed

4 files changed

+63
-40
lines changed

include/my_alloc.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ typedef struct st_mem_root
6060
PSI_memory_key psi_key;
6161
} MEM_ROOT;
6262

63+
typedef struct st_mem_root_savepoint
64+
{
65+
MEM_ROOT *root;
66+
USED_MEM *free;
67+
USED_MEM *used;
68+
unsigned short first_block_usage;
69+
} MEM_ROOT_SAVEPOINT;
70+
6371
#ifdef __cplusplus
6472
}
6573
#endif

include/my_sys.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -921,8 +921,8 @@ extern void move_root(MEM_ROOT *to, MEM_ROOT *from);
921921
extern void set_prealloc_root(MEM_ROOT *root, char *ptr);
922922
extern void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
923923
size_t prealloc_size);
924-
extern USED_MEM *get_last_memroot_block(MEM_ROOT* root);
925-
extern void free_all_new_blocks(MEM_ROOT *root, USED_MEM *last_block);
924+
extern void root_make_savepoint(MEM_ROOT *root, MEM_ROOT_SAVEPOINT *sv);
925+
extern void root_free_to_savepoint(const MEM_ROOT_SAVEPOINT *sv);
926926
extern void protect_root(MEM_ROOT *root, int prot);
927927
extern char *strdup_root(MEM_ROOT *root,const char *str);
928928
static inline char *safe_strdup_root(MEM_ROOT *root, const char *str)

mysys/my_alloc.c

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
266266
{
267267
size_t get_size, block_size;
268268
uchar* point;
269-
reg1 USED_MEM *next= 0;
270-
reg2 USED_MEM **prev;
269+
USED_MEM *next= 0;
270+
USED_MEM **prev;
271271
size_t original_length __attribute__((unused)) = length;
272272
DBUG_ENTER("alloc_root");
273273
DBUG_PRINT("enter",("root: %p", mem_root));
@@ -425,10 +425,10 @@ void *multi_alloc_root(MEM_ROOT *root, ...)
425425
#if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG))
426426
/** Mark all data in blocks free for reusage */
427427

428-
static inline void mark_blocks_free(MEM_ROOT* root)
428+
static void mark_blocks_free(MEM_ROOT* root)
429429
{
430-
reg1 USED_MEM *next;
431-
reg2 USED_MEM **last;
430+
USED_MEM *next;
431+
USED_MEM **last;
432432

433433
/* iterate through (partially) free blocks, mark them free */
434434
last= &root->free;
@@ -451,7 +451,6 @@ static inline void mark_blocks_free(MEM_ROOT* root)
451451
/* Now everything is set; Indicate that nothing is used anymore */
452452
root->used= 0;
453453
root->first_block_usage= 0;
454-
root->block_num= 4;
455454
}
456455
#endif
457456

@@ -477,7 +476,7 @@ static inline void mark_blocks_free(MEM_ROOT* root)
477476

478477
void free_root(MEM_ROOT *root, myf MyFlags)
479478
{
480-
reg1 USED_MEM *next,*old;
479+
USED_MEM *next,*old;
481480
DBUG_ENTER("free_root");
482481
DBUG_PRINT("enter",("root: %p flags: %lu", root, MyFlags));
483482

@@ -567,45 +566,61 @@ void move_root(MEM_ROOT *to, MEM_ROOT *from)
567566
from->used= 0;
568567
}
569568

570-
571-
572569
/*
573-
Remember last MEM_ROOT block.
570+
Prepare MEM_ROOT to a later truncation. Everything allocated after
571+
that point can be freed while keeping earlier allocations intact.
574572
575-
This allows one to free all new allocated blocks.
573+
For this to work we cannot allow new allocations in partially filled blocks,
574+
so remove all non-empty blocks from the memroot. For simplicity, let's
575+
also remove all used blocks.
576576
*/
577-
578-
USED_MEM *get_last_memroot_block(MEM_ROOT* root)
577+
void root_make_savepoint(MEM_ROOT *root, MEM_ROOT_SAVEPOINT *sv)
579578
{
580-
return root->used ? root->used : root->pre_alloc;
579+
USED_MEM **prev= &root->free, *block= *prev;
580+
for ( ; block; prev= &block->next, block= *prev)
581+
if (block->left < block->size - ALIGN_SIZE(sizeof(USED_MEM)))
582+
break;
583+
sv->root= root;
584+
sv->free= block;
585+
sv->used= root->used;
586+
sv->first_block_usage= root->first_block_usage;
587+
*prev= 0;
588+
root->used= 0;
581589
}
582590

583591
/*
584-
Free all newly allocated blocks
585-
*/
592+
Restore MEM_ROOT to the state before the savepoint was made.
586593
587-
void free_all_new_blocks(MEM_ROOT *root, USED_MEM *last_block)
594+
Restore old free and used lists.
595+
Mark all new (after savepoint) used and partially used blocks free
596+
and put them into the free list.
597+
*/
598+
void root_free_to_savepoint(const MEM_ROOT_SAVEPOINT *sv)
588599
{
589-
USED_MEM *old, *next;
590-
if (!root->used)
591-
return; /* Nothing allocated */
592-
return;
593-
/*
594-
Free everying allocated up to, but not including, last_block.
595-
However do not go past pre_alloc as we do not want to free
596-
that one. This should not be a problem as in almost all normal
597-
usage pre_alloc is last in the list.
598-
*/
600+
MEM_ROOT *root= sv->root;
601+
USED_MEM **prev= &root->free, *block= *prev;
599602

600-
for (next= root->used ;
601-
next && next != last_block && next != root->pre_alloc ; )
603+
/* iterate through (partially) free blocks, mark them free */
604+
for ( ; block; prev= &block->next, block= *prev)
602605
{
603-
old= next; next= next->next;
604-
root_free(root, old, old->size);
606+
block->left= block->size - ALIGN_SIZE(sizeof(USED_MEM));
607+
TRASH_MEM(block);
605608
}
606-
root->used= next;
607-
root->block_num= 4;
608-
root->first_block_usage= 0;
609+
610+
/* Combine the free and the used list */
611+
*prev= block=root->used;
612+
613+
/* now go through the used blocks and mark them free */
614+
for ( ; block; prev= &block->next, block= *prev)
615+
{
616+
block->left= block->size - ALIGN_SIZE(sizeof(USED_MEM));
617+
TRASH_MEM(block);
618+
}
619+
620+
/* restore free and used lists from savepoint */
621+
*prev= sv->free;
622+
root->used= sv->used;
623+
root->first_block_usage= prev == &root->free ? sv->first_block_usage : 0;
609624
}
610625

611626
/**
@@ -615,7 +630,7 @@ void free_all_new_blocks(MEM_ROOT *root, USED_MEM *last_block)
615630
#if defined(HAVE_MMAP) && defined(HAVE_MPROTECT) && defined(MAP_ANONYMOUS)
616631
void protect_root(MEM_ROOT *root, int prot)
617632
{
618-
reg1 USED_MEM *next,*old;
633+
USED_MEM *next,*old;
619634
DBUG_ENTER("protect_root");
620635
DBUG_PRINT("enter",("root: %p prot: %d", root, prot));
621636

sql/sql_admin.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
982982
{
983983
TABLE *tab= table->table;
984984
Field **field_ptr= tab->field;
985-
USED_MEM *memroot_block;
985+
MEM_ROOT_SAVEPOINT memroot_sv;
986986

987987
if (!lex->column_list)
988988
{
@@ -1084,15 +1084,15 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
10841084
}
10851085
/* Ensure that number of records are updated */
10861086
tab->file->info(HA_STATUS_VARIABLE);
1087-
memroot_block= get_last_memroot_block(thd->mem_root);
1087+
root_make_savepoint(thd->mem_root, &memroot_sv);
10881088
if (!(compl_result_code=
10891089
alloc_statistics_for_table(thd, tab,
10901090
&tab->has_value_set)) &&
10911091
!(compl_result_code=
10921092
collect_statistics_for_table(thd, tab)))
10931093
compl_result_code= update_statistics_for_table(thd, tab);
10941094
free_statistics_for_table(tab);
1095-
free_all_new_blocks(thd->mem_root, memroot_block);
1095+
root_free_to_savepoint(&memroot_sv);
10961096
}
10971097
else
10981098
compl_result_code= HA_ADMIN_FAILED;

0 commit comments

Comments
 (0)