2018-04-11 18:37:55 +00:00
|
|
|
#ifndef MEM_POOL_H
|
|
|
|
#define MEM_POOL_H
|
|
|
|
|
|
|
|
struct mp_block {
|
|
|
|
struct mp_block *next_block;
|
|
|
|
char *next_free;
|
|
|
|
char *end;
|
|
|
|
uintmax_t space[FLEX_ARRAY]; /* more */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct mem_pool {
|
|
|
|
struct mp_block *mp_block;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The amount of available memory to grow the pool by.
|
|
|
|
* This size does not include the overhead for the mp_block.
|
|
|
|
*/
|
|
|
|
size_t block_alloc;
|
|
|
|
|
|
|
|
/* The total amount of memory allocated by the pool. */
|
|
|
|
size_t pool_alloc;
|
|
|
|
};
|
|
|
|
|
2018-07-02 19:49:34 +00:00
|
|
|
/*
|
|
|
|
* Initialize mem_pool with specified initial size.
|
|
|
|
*/
|
mem-pool: use more standard initialization and finalization
A typical memory type, such as strbuf, hashmap, or string_list can be
stored on the stack or embedded within another structure. mem_pool
cannot be, because of how mem_pool_init() and mem_pool_discard() are
written. mem_pool_init() does essentially the following (simplified
for purposes of explanation here):
void mem_pool_init(struct mem_pool **pool...)
{
*pool = xcalloc(1, sizeof(*pool));
It seems weird to require that mem_pools can only be accessed through a
pointer. It also seems slightly dangerous: unlike strbuf_release() or
strbuf_reset() or string_list_clear(), all of which put the data
structure into a state where it can be re-used after the call,
mem_pool_discard(pool) will leave pool pointing at free'd memory.
read-cache (and split-index) are the only current users of mem_pools,
and they haven't fallen into a use-after-free mistake here, but it seems
likely to be problematic for future users especially since several of
the current callers of mem_pool_init() will only call it when the
mem_pool* is not already allocated (i.e. is NULL).
This type of mechanism also prevents finding synchronization
points where one can free existing memory and then resume more
operations. It would be natural at such points to run something like
mem_pool_discard(pool...);
and, if necessary,
mem_pool_init(&pool...);
and then carry on continuing to use the pool. However, this fails badly
if several objects had a copy of the value of pool from before these
commands; in such a case, those objects won't get the updated value of
pool that mem_pool_init() overwrites pool with and they'll all instead
be reading and writing from free'd memory.
Modify mem_pool_init()/mem_pool_discard() to behave more like
strbuf_init()/strbuf_release()
or
string_list_init()/string_list_clear()
In particular: (1) make mem_pool_init() just take a mem_pool* and have
it only worry about allocating struct mp_blocks, not the struct mem_pool
itself, (2) make mem_pool_discard() free the memory that the pool was
responsible for, but leave it in a state where it can be used to
allocate more memory afterward (without the need to call mem_pool_init()
again).
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-08-15 17:37:56 +00:00
|
|
|
void mem_pool_init(struct mem_pool *pool, size_t initial_size);
|
2018-07-02 19:49:34 +00:00
|
|
|
|
|
|
|
/*
|
mem-pool: use more standard initialization and finalization
A typical memory type, such as strbuf, hashmap, or string_list can be
stored on the stack or embedded within another structure. mem_pool
cannot be, because of how mem_pool_init() and mem_pool_discard() are
written. mem_pool_init() does essentially the following (simplified
for purposes of explanation here):
void mem_pool_init(struct mem_pool **pool...)
{
*pool = xcalloc(1, sizeof(*pool));
It seems weird to require that mem_pools can only be accessed through a
pointer. It also seems slightly dangerous: unlike strbuf_release() or
strbuf_reset() or string_list_clear(), all of which put the data
structure into a state where it can be re-used after the call,
mem_pool_discard(pool) will leave pool pointing at free'd memory.
read-cache (and split-index) are the only current users of mem_pools,
and they haven't fallen into a use-after-free mistake here, but it seems
likely to be problematic for future users especially since several of
the current callers of mem_pool_init() will only call it when the
mem_pool* is not already allocated (i.e. is NULL).
This type of mechanism also prevents finding synchronization
points where one can free existing memory and then resume more
operations. It would be natural at such points to run something like
mem_pool_discard(pool...);
and, if necessary,
mem_pool_init(&pool...);
and then carry on continuing to use the pool. However, this fails badly
if several objects had a copy of the value of pool from before these
commands; in such a case, those objects won't get the updated value of
pool that mem_pool_init() overwrites pool with and they'll all instead
be reading and writing from free'd memory.
Modify mem_pool_init()/mem_pool_discard() to behave more like
strbuf_init()/strbuf_release()
or
string_list_init()/string_list_clear()
In particular: (1) make mem_pool_init() just take a mem_pool* and have
it only worry about allocating struct mp_blocks, not the struct mem_pool
itself, (2) make mem_pool_discard() free the memory that the pool was
responsible for, but leave it in a state where it can be used to
allocate more memory afterward (without the need to call mem_pool_init()
again).
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-08-15 17:37:56 +00:00
|
|
|
* Discard all the memory the memory pool is responsible for.
|
2018-07-02 19:49:34 +00:00
|
|
|
*/
|
2020-08-15 17:37:57 +00:00
|
|
|
void mem_pool_discard(struct mem_pool *pool, int invalidate_memory);
|
2018-07-02 19:49:34 +00:00
|
|
|
|
2018-04-11 18:37:55 +00:00
|
|
|
/*
|
|
|
|
* Alloc memory from the mem_pool.
|
|
|
|
*/
|
|
|
|
void *mem_pool_alloc(struct mem_pool *pool, size_t len);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate and zero memory from the memory pool.
|
|
|
|
*/
|
|
|
|
void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size);
|
|
|
|
|
2020-08-15 17:37:55 +00:00
|
|
|
/*
|
|
|
|
* Allocate memory from the memory pool and copy str into it.
|
|
|
|
*/
|
|
|
|
char *mem_pool_strdup(struct mem_pool *pool, const char *str);
|
|
|
|
char *mem_pool_strndup(struct mem_pool *pool, const char *str, size_t len);
|
|
|
|
|
2024-02-25 11:39:44 +00:00
|
|
|
/*
|
|
|
|
* Allocate memory from the memory pool and format a string into it.
|
|
|
|
*/
|
2024-06-08 18:37:47 +00:00
|
|
|
__attribute__((format (printf, 2, 3)))
|
2024-02-25 11:39:44 +00:00
|
|
|
char *mem_pool_strfmt(struct mem_pool *pool, const char *fmt, ...);
|
|
|
|
|
2018-07-02 19:49:35 +00:00
|
|
|
/*
|
|
|
|
* Move the memory associated with the 'src' pool to the 'dst' pool. The 'src'
|
|
|
|
* pool will be empty and not contain any memory. It still needs to be free'd
|
|
|
|
* with a call to `mem_pool_discard`.
|
|
|
|
*/
|
|
|
|
void mem_pool_combine(struct mem_pool *dst, struct mem_pool *src);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if a memory pointed at by 'mem' is part of the range of
|
|
|
|
* memory managed by the specified mem_pool.
|
|
|
|
*/
|
2020-08-15 17:37:57 +00:00
|
|
|
int mem_pool_contains(struct mem_pool *pool, void *mem);
|
2018-07-02 19:49:35 +00:00
|
|
|
|
2018-04-11 18:37:55 +00:00
|
|
|
#endif
|