diff --git a/include/common/memory.h b/include/common/memory.h index 3a0eaf695..965dfc488 100644 --- a/include/common/memory.h +++ b/include/common/memory.h @@ -61,10 +61,15 @@ static inline void pool_destroy(void **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. +/* Allocates new entries for pool until there are at least + 1 + * available, then returns the last one for immediate use, so that at least + * are left available in the pool upon return. NULL is returned if the + * last entry could not be allocated. It's important to note that at least one + * allocation is always performed even if there are enough entries in the pool. + * A call to the garbage collector is performed at most once in case malloc() + * returns an error, before returning NULL. */ -void *pool_refill_alloc(struct pool_head *pool); +void *pool_refill_alloc(struct pool_head *pool, unsigned int avail); /* Try to find an existing shared pool with the same characteristics and * returns it, otherwise creates this one. NULL is returned if no memory @@ -121,7 +126,7 @@ static inline void *pool_alloc_dirty(struct pool_head *pool) void *p; if ((p = pool_get_first(pool)) == NULL) - p = pool_refill_alloc(pool); + p = pool_refill_alloc(pool, 0); return p; } diff --git a/src/memory.c b/src/memory.c index 3fbc52059..61c150bfd 100644 --- a/src/memory.c +++ b/src/memory.c @@ -79,26 +79,42 @@ 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. +/* Allocates new entries for pool until there are at least + 1 + * available, then returns the last one for immediate use, so that at least + * are left available in the pool upon return. NULL is returned if the + * last entry could not be allocated. It's important to note that at least one + * allocation is always performed even if there are enough entries in the pool. + * A call to the garbage collector is performed at most once in case malloc() + * returns an error, before returning NULL. */ -void *pool_refill_alloc(struct pool_head *pool) +void *pool_refill_alloc(struct pool_head *pool, unsigned int avail) { - void *ret; + void *ptr = NULL; + int failed = 0; - if (pool->limit && (pool->allocated >= pool->limit)) - return NULL; - ret = CALLOC(1, pool->size); - if (!ret) { - pool_gc2(); - ret = CALLOC(1, pool->size); - if (!ret) + /* stop point */ + avail += pool->used; + + while (1) { + if (pool->limit && pool->allocated >= pool->limit) return NULL; + + ptr = MALLOC(pool->size); + if (!ptr) { + if (failed) + return NULL; + failed++; + pool_gc2(); + continue; + } + if (++pool->allocated > avail) + break; + + *(void **)ptr = (void *)pool->free_list; + pool->free_list = ptr; } - pool->allocated++; pool->used++; - return ret; + return ptr; } /*