]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: data-stack - Keep the largest unused block in memory after all
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 21 May 2021 15:13:08 +0000 (18:13 +0300)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Mon, 24 May 2021 11:52:01 +0000 (11:52 +0000)
This reverts 2da21080dbbe50b4924ac0135c84babfb404dcce, but that alone
doesn't produce working code so there are also some further fixups.

src/lib/data-stack.c
src/lib/data-stack.h
src/lib/test-data-stack.c

index fe48107ebab8eaffa9f84f96be07fd9555a01633..801c4e99d700479e79695e80ebd50c1a67aaf624 100644 (file)
@@ -82,6 +82,10 @@ static struct stack_frame *current_frame;
 /* The latest block currently used for allocation. current_block->next is
    always NULL. */
 static struct stack_block *current_block;
+/* The largest block that data stack has allocated so far, which was already
+   freed. This can prevent rapid malloc()+free()ing when data stack is grown
+   and shrunk constantly. */
+static struct stack_block *unused_block = NULL;
 
 static struct event *event_datastack = NULL;
 static bool event_datastack_deinitialized = FALSE;
@@ -198,7 +202,8 @@ static void free_blocks(struct stack_block *block)
 {
        struct stack_block *next;
 
-       /* free all the blocks */
+       /* free all the blocks, except if any of them is bigger than
+          unused_block, replace it */
        while (block != NULL) {
                block_canary_check(block);
                next = block->next;
@@ -207,8 +212,15 @@ static void free_blocks(struct stack_block *block)
                memset(STACK_BLOCK_DATA(block), CLEAR_CHR, block->size);
 #endif
 
-               if (block != &outofmem_area.block)
+               if (block == &outofmem_area.block)
+                       ;
+               else if (unused_block == NULL ||
+                        block->size > unused_block->size) {
+                       free(unused_block);
+                       unused_block = block;
+               } else {
                        free(block);
+               }
 
                block = next;
        }
@@ -328,6 +340,16 @@ bool t_pop_pass_str(data_stack_frame_t *id, const char **str)
        return ret;
 }
 
+static void mem_block_reset(struct stack_block *block)
+{
+       block->prev = NULL;
+       block->next = NULL;
+       block->left = block->size;
+#ifdef DEBUG
+       block->left_lowwater = block->size;
+#endif
+}
+
 static struct stack_block *mem_block_alloc(size_t min_size)
 {
        struct stack_block *block;
@@ -352,13 +374,9 @@ static struct stack_block *mem_block_alloc(size_t min_size)
                        alloc_size + SIZEOF_MEMBLOCK);
        }
        block->size = alloc_size;
-       block->left = block->size;
-       block->prev = NULL;
-       block->next = NULL;
        block->canary = BLOCK_CANARY;
-
+       mem_block_reset(block);
 #ifdef DEBUG
-       block->left_lowwater = block->size;
        memset(STACK_BLOCK_DATA(block), CLEAR_CHR, alloc_size);
 #endif
        return block;
@@ -443,9 +461,16 @@ static void *t_malloc_real(size_t size, bool permanent)
        if (current_block->left < alloc_size) {
                struct stack_block *block;
 
-               /* current block is full, allocate a new one */
-               block = mem_block_alloc(alloc_size);
-               warn = TRUE;
+               /* current block is full, see if we can use the unused_block */
+               if (unused_block != NULL && unused_block->size >= alloc_size) {
+                       block = unused_block;
+                       unused_block = NULL;
+                       mem_block_reset(block);
+               } else {
+                       /* current block is full, allocate a new one */
+                       block = mem_block_alloc(alloc_size);
+                       warn = TRUE;
+               }
 
                /* The newly allocated block will replace the current_block,
                   i.e. current_block always points to the last element in
@@ -675,6 +700,12 @@ size_t data_stack_get_used_size(void)
        return size;
 }
 
+void data_stack_free_unused(void)
+{
+       free(unused_block);
+       unused_block = NULL;
+}
+
 void data_stack_init(void)
 {
        if (data_stack_initialized) {
@@ -712,4 +743,5 @@ void data_stack_deinit(void)
 
        free(current_block);
        current_block = NULL;
+       data_stack_free_unused();
 }
index 1b92659cd73e966e50172d3dbacebe74873c38a3..ba2b566431dfc626756eb1547f48fbace3aabc19 100644 (file)
@@ -154,6 +154,10 @@ size_t data_stack_get_alloc_size(void);
 /* Returns the number of bytes currently used in data stack. */
 size_t data_stack_get_used_size(void);
 
+/* Free all the memory that is currently unused (i.e. reserved for growing
+   data stack quickly). */
+void data_stack_free_unused(void);
+
 void data_stack_init(void);
 void data_stack_deinit_event(void);
 void data_stack_deinit(void);
index 886929c4c60e2aa9891550c8fc8a0b99e3cb3010..61a7d8e3c0c5786a308d620f321b0df71cc61318 100644 (file)
@@ -54,6 +54,9 @@ static void test_ds_grow_event(void)
        event_set_global_debug_log_filter(filter);
        event_filter_unref(&filter);
 
+       /* make sure the test won't fail due to earlier data stack
+          allocations. */
+       data_stack_free_unused();
        T_BEGIN {
                (void)t_malloc0(1024*5);
                test_assert(ds_grow_event_count == 0);