From edc2c760704611bdd66ef2040ea87631a7a0115b Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 3 Dec 2014 15:25:28 +0100 Subject: MEDIUM: memory: improve pool_refill_alloc() to pass a refill count Till now this function would only allocate one entry at a time. But now with dynamic buffers sometimes we'd like to allocate the number of missing entries to properly refill the pool. Let's modify it to take a count. This ensures that we don't move the pointers back and forth between the user pointer and the pool, and that we don't call pool_gc2() for each failed malloc. Instead, it's called only once and the malloc is only allowed to fail once. --- include/common/buffer.h | 2 +- include/common/memory.h | 9 +++++---- src/memory.c | 43 +++++++++++++++++++++++++++++-------------- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/include/common/buffer.h b/include/common/buffer.h index e2f4707..6154522 100644 --- a/include/common/buffer.h +++ b/include/common/buffer.h @@ -464,7 +464,7 @@ static inline struct buffer *b_alloc_single(struct buffer **buf) * we need to check if it's possible to refill the pool otherwise * we must return the one we picked. */ - next = pool_refill_alloc(pool2_buffer); + next = pool_refill_alloc(pool2_buffer, 2); pool_free2(pool2_buffer, next); if ((pool2_buffer->allocated - pool2_buffer->used) >= 2) return *buf; diff --git a/include/common/memory.h b/include/common/memory.h index e446d81..259e4ff 100644 --- a/include/common/memory.h +++ b/include/common/memory.h @@ -132,10 +132,11 @@ struct pool_head { /* poison each newly allocated area with this byte if not null */ extern char mem_poison_byte; -/* Allocate a new entry for pool , and return it for immediate use. - * NULL is returned if no memory is available for a new creation. +/* Allocate new entry for pool , and return the last one if + * successful for immediate use. NULL is returned if the last entry could + * not be allocated. */ -void *pool_refill_alloc(struct pool_head *pool); +void *pool_refill_alloc(struct pool_head *pool, int count); /* Try to find an existing shared pool with the same characteristics and * returns it, otherwise creates this one. NULL is returned if no memory @@ -175,7 +176,7 @@ void *pool_destroy2(struct pool_head *pool); ({ \ void *__p; \ if ((__p = (pool)->free_list) == NULL) \ - __p = pool_refill_alloc(pool); \ + __p = pool_refill_alloc(pool, 1); \ else { \ (pool)->free_list = *(void **)(pool)->free_list;\ (pool)->used++; \ diff --git a/src/memory.c b/src/memory.c index 1e62259..7ebc657 100644 --- a/src/memory.c +++ b/src/memory.c @@ -79,28 +79,43 @@ struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags) return pool; } -/* Allocate a new entry for pool , and return it for immediate use. - * NULL is returned if no memory is available for a new creation. A call - * to the garbage collector is performed before returning NULL. +/* Allocate new entry for pool , and return the last one if + * successful for immediate use. NULL is returned if the last entry could + * not be allocated. A call to the garbage collector is performed at most + * once before returning NULL. */ -void *pool_refill_alloc(struct pool_head *pool) +void *pool_refill_alloc(struct pool_head *pool, int count) { - void *ret; + void *ptr = NULL; + int failed = 0; - if (pool->limit && (pool->allocated >= pool->limit)) + if (count <= 0 || + (pool->limit && (pool->allocated + count >= pool->limit))) return NULL; - ret = CALLOC(1, pool->size); - if (!ret) { - pool_gc2(); - ret = CALLOC(1, pool->size); - if (!ret) - return NULL; + + while (1) { + ptr = MALLOC(pool->size); + if (!ptr) { + if (failed) + return NULL; + failed++; + pool_gc2(); + continue; + } + if (!--count) + break; + + *(void **)ptr = (void *)pool->free_list; + pool->free_list = ptr; + pool->allocated++; } + + /* all entries were properly allocated, ptr is valid */ if (mem_poison_byte) - memset(ret, mem_poison_byte, pool->size); + memset(ptr, mem_poison_byte, pool->size); pool->allocated++; pool->used++; - return ret; + return ptr; } /* -- 1.7.12.1