commit abcbf778676ee0e081e4fa99443f4348828d6ab6
parent 899730e8c70cd7679394d5c940e8ad953eea262b
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date: Sat, 9 Oct 2021 17:24:04 +0300
Partially reworked memory allocation from the pool
More robust implementation, should catch non-standard situations.
Always track read and write buffers, even if size is zero as
buffer pointers are used for data size calculations.
Diffstat:
3 files changed, 100 insertions(+), 57 deletions(-)
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
@@ -204,16 +204,12 @@ connection_alloc_memory (struct MHD_Connection *connection,
{
struct MHD_Connection *const c = connection; /* a short alias */
struct MemoryPool *const pool = c->pool; /* a short alias */
- size_t required_free_size; /**< The required amount of free memory */
- size_t pool_free; /**< The amount of free memory in the pool */
+ size_t need_to_free; /**< The required amount of free memory */
void *res;
- required_free_size = MHD_pool_alloc_size (size);
- pool_free = MHD_pool_get_free (pool);
- if (pool_free < required_free_size)
+ res = MHD_pool_try_alloc (pool, size, &need_to_free);
+ if (NULL == res)
{
- size_t need_to_free = required_free_size - pool_free;
- mhd_assert (MHD_pool_alloc_size (need_to_free) == need_to_free);
if (NULL != c->write_buffer)
{
/* The connection is in the sending phase */
@@ -227,12 +223,10 @@ connection_alloc_memory (struct MHD_Connection *connection,
c->write_buffer_size,
new_buf_size);
mhd_assert (c->write_buffer == buf);
-#ifdef NDEBUG
- (void) buf; /* mute compiler warning */
-#endif
mhd_assert (c->write_buffer_append_offset <= new_buf_size);
mhd_assert (c->write_buffer_send_offset <= new_buf_size);
c->write_buffer_size = new_buf_size;
+ c->write_buffer = buf;
}
else
return NULL;
@@ -249,20 +243,18 @@ connection_alloc_memory (struct MHD_Connection *connection,
c->read_buffer_size,
new_buf_size);
mhd_assert (c->read_buffer == buf);
-#ifdef NDEBUG
- (void) buf; /* mute compiler warning */
-#endif
mhd_assert (c->read_buffer_offset <= new_buf_size);
c->read_buffer_size = new_buf_size;
+ c->read_buffer = buf;
}
else
return NULL;
}
else
return NULL;
+ res = MHD_pool_allocate (pool, size, true);
+ mhd_assert (NULL != res); /* It has been checked that pool has enough space */
}
- res = MHD_pool_allocate (pool, size, true);
- mhd_assert (NULL != res); /* It has been checked that pool has enough space */
return res;
}
@@ -1486,7 +1478,7 @@ try_grow_read_buffer (struct MHD_Connection *connection,
/**
- * Shrink connection read buffer to the zero of data in the buffer
+ * Shrink connection read buffer to the zero size of free space in the buffer
* @param connection the connection whose read buffer is being manipulated
*/
static void
@@ -1495,11 +1487,10 @@ connection_shrink_read_buffer (struct MHD_Connection *connection)
struct MHD_Connection *const c = connection; /**< a short alias */
void *new_buf;
- if (NULL == c->read_buffer)
+ if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
{
mhd_assert (0 == c->read_buffer_size);
mhd_assert (0 == c->read_buffer_offset);
- c->read_buffer = NULL;
return;
}
@@ -1507,12 +1498,8 @@ connection_shrink_read_buffer (struct MHD_Connection *connection)
new_buf = MHD_pool_reallocate (c->pool, c->read_buffer, c->read_buffer_size,
c->read_buffer_offset);
mhd_assert (c->read_buffer == new_buf);
-#ifdef NDEBUG
- (void) new_buf; /* squash compiler warning */
-#endif /* NDEBUG */
+ c->read_buffer = new_buf;
c->read_buffer_size = c->read_buffer_offset;
- if (0 == c->read_buffer_size)
- c->read_buffer = NULL;
}
@@ -1582,16 +1569,25 @@ connection_shrink_write_buffer (struct MHD_Connection *connection)
mhd_assert (c->write_buffer_append_offset >= c->write_buffer_send_offset);
mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset);
- if (NULL == c->write_buffer)
+ if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size))
+ {
+ mhd_assert (0 == c->write_buffer_append_offset);
+ mhd_assert (0 == c->write_buffer_send_offset);
+ c->write_buffer = NULL;
return;
+ }
if (c->write_buffer_append_offset == c->write_buffer_size)
return;
new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size,
c->write_buffer_append_offset);
- mhd_assert (c->write_buffer == new_buf);
- (void) new_buf; /* squash compiler warning */
+ mhd_assert ((c->write_buffer == new_buf) || \
+ (0 == c->write_buffer_append_offset));
c->write_buffer_size = c->write_buffer_append_offset;
+ if (0 == c->write_buffer_size)
+ c->write_buffer = NULL;
+ else
+ c->write_buffer = new_buf;
}
@@ -1987,8 +1983,9 @@ build_header_response (struct MHD_Connection *connection)
buf = c->write_buffer;
pos = c->write_buffer_append_offset;
buf_size = c->write_buffer_size;
- if ((NULL == buf) || (0 == buf_size))
+ if (0 == buf_size)
return MHD_NO;
+ mhd_assert (NULL != buf);
/* * The status line * */
diff --git a/src/microhttpd/memorypool.c b/src/microhttpd/memorypool.c
@@ -105,22 +105,6 @@ MHD_init_mem_pools_ (void)
/**
- * Get the real size that would be allocated by the memory pool when
- * requested to allocate @a size.
- * @param size the size of memory area that would be rounded up to the
- * allocation granularity
- * @return the size that would be allocated by #MHD_pool_allocate() when
- * requested to allocate @a size. It is also minimal size of free
- * space in the pool required to #MHD_pool_allocate() succeed.
- */
-size_t
-MHD_pool_alloc_size (size_t size)
-{
- return ROUND_TO_ALIGN (size);
-}
-
-
-/**
* Handle for a memory pool. Pools are not reentrant and must not be
* used by multiple threads.
*/
@@ -312,6 +296,56 @@ MHD_pool_allocate (struct MemoryPool *pool,
/**
+ * Try to allocate @a size bytes memory area from the @a pool.
+ *
+ * If allocation fails, @a required_bytes is updated with size required to be
+ * freed in the @a pool from relocatable area to allocate requested number
+ * of bytes.
+ * Allocated memory area is always not rellocatable ("from end").
+ *
+ * @param pool memory pool to use for the operation
+ * @param size the size of memory in bytes to allocate
+ * @param[out] required_bytes the pointer to variable to be updated with
+ * the size of the required additional free
+ * memory area, not updated if function succeed.
+ * Cannot be NULL.
+ * @return the pointer to allocated memory area if succeed,
+ * NULL if the pool doesn't have enough space, required_bytes is updated
+ * with amount of space needed to be freed in relocatable area or
+ * set to SIZE_MAX if requested size is too large for the pool.
+ */
+void *
+MHD_pool_try_alloc (struct MemoryPool *pool,
+ size_t size,
+ size_t *required_bytes)
+{
+ void *ret;
+ size_t asize;
+
+ mhd_assert (pool->end >= pool->pos);
+ mhd_assert (pool->size >= pool->end - pool->pos);
+ asize = ROUND_TO_ALIGN (size);
+ if ( (0 == asize) && (0 != size) )
+ { /* size is too close to SIZE_MAX, very unlikely */
+ *required_bytes = SIZE_MAX;
+ return NULL;
+ }
+ if ( (pool->pos + asize > pool->end) ||
+ (pool->pos + asize < pool->pos))
+ {
+ if (asize <= pool->end)
+ *required_bytes = asize - (pool->end - pool->pos);
+ else
+ *required_bytes = SIZE_MAX;
+ return NULL;
+ }
+ ret = &pool->memory[pool->end - asize];
+ pool->end -= asize;
+ return ret;
+}
+
+
+/**
* Reallocate a block of memory obtained from the pool.
* This is particularly efficient when growing or
* shrinking the block that was last (re)allocated.
@@ -355,7 +389,7 @@ MHD_pool_reallocate (struct MemoryPool *pool,
pool->end);
if (0 != old_size)
- { /* Need to save some data */
+ { /* Have previously allocated data */
const size_t old_offset = (uint8_t*) old - pool->memory;
const bool shrinking = (old_size > new_size);
/* Try resizing in-place */
diff --git a/src/microhttpd/memorypool.h b/src/microhttpd/memorypool.h
@@ -53,19 +53,6 @@ MHD_init_mem_pools_ (void);
/**
- * Get the real size that would be allocated by the memory pool when
- * requested to allocate @a size.
- * @param size the size of memory area that would be rounded up to the
- * allocation granularity
- * @return the size that would be allocated by #MHD_pool_allocate() when
- * requested to allocate @a size. It is also minimal size of free
- * space in the pool required to #MHD_pool_allocate() succeed.
- */
-size_t
-MHD_pool_alloc_size (size_t size);
-
-
-/**
* Create a memory pool.
*
* @param max maximum size of the pool
@@ -102,6 +89,31 @@ MHD_pool_allocate (struct MemoryPool *pool,
/**
+ * Try to allocate @a size bytes memory area from the @a pool.
+ *
+ * If allocation fails, @a required_bytes is updated with size required to be
+ * freed in the @a pool from relocatable area to allocate requested number
+ * of bytes.
+ * Allocated memory area is always not rellocatable ("from end").
+ *
+ * @param pool memory pool to use for the operation
+ * @param size the size of memory in bytes to allocate
+ * @param[out] required_bytes the pointer to variable to be updated with
+ * the size of the required additional free
+ * memory area, not updated if function succeed.
+ * Cannot be NULL.
+ * @return the pointer to allocated memory area if succeed,
+ * NULL if the pool doesn't have enough space, required_bytes is updated
+ * with amount of space needed to be freed in relocatable area or
+ * set to SIZE_MAX if requested size is too large for the pool.
+ */
+void *
+MHD_pool_try_alloc (struct MemoryPool *pool,
+ size_t size,
+ size_t *required_bytes);
+
+
+/**
* Reallocate a block of memory obtained from the pool.
* This is particularly efficient when growing or
* shrinking the block that was last (re)allocated.