#include "cryptobox.h"
#include "contrib/uthash/utlist.h"
#include "mem_pool_internal.h"
+#include "heap.h"
#ifdef WITH_JEMALLOC
#include <jemalloc/jemalloc.h>
size = entry->cur_suggestion;
}
+ /* Adjust size based on pool type */
+ if (flags & RSPAMD_MEMPOOL_LONG_LIVED) {
+ /* Long-lived pools: use larger pages to reduce fragmentation */
+ if (size < 16384) {
+ size = 16384; /* 16KB minimum for long-lived pools */
+ }
+ }
+ else if (flags & RSPAMD_MEMPOOL_SHORT_LIVED) {
+ /* Short-lived pools: optimize for quick allocation/deallocation */
+ if (entry && entry->cur_suggestion > 0) {
+ /* Use entry point statistics for short-lived pools */
+ size = entry->cur_suggestion;
+ }
+ else if (size == 0) {
+ /* Default to smaller size for short-lived */
+ size = 4096;
+ }
+ }
+
total_size = sizeof(rspamd_mempool_t) +
sizeof(struct rspamd_mempool_specific) +
MIN_MEM_ALIGNMENT +
rspamd_mempool_destruct_t func,
void *data,
const char *function,
- const char *line)
+ const char *line,
+ unsigned int priority)
{
- struct _pool_destructors *cur;
+ struct _pool_destructors *dtor;
+ struct _pool_destructor_heap_elt *heap_wrapper;
POOL_MTX_LOCK();
- cur = rspamd_mempool_alloc_(pool, sizeof(*cur),
- RSPAMD_ALIGNOF(struct _pool_destructors), line);
- cur->func = func;
- cur->data = data;
- cur->function = function;
- cur->loc = line;
- cur->next = NULL;
-
- if (pool->priv->dtors_tail) {
- pool->priv->dtors_tail->next = cur;
- pool->priv->dtors_tail = cur;
- }
- else {
- pool->priv->dtors_head = cur;
- pool->priv->dtors_tail = cur;
+
+ /* Allocate destructor structure */
+ dtor = rspamd_mempool_alloc_(pool, sizeof(*dtor),
+ RSPAMD_ALIGNOF(struct _pool_destructors), line);
+ dtor->func = func;
+ dtor->data = data;
+ dtor->function = function;
+ dtor->loc = line;
+ dtor->priority = priority;
+
+ /* Initialize heap if not yet created */
+ if (pool->priv->dtors_heap == NULL) {
+ gsize reserved_size = 8;
+
+ /* Use entry point statistics if available */
+ if (pool->priv->entry && pool->priv->entry->cur_dtors > 0) {
+ reserved_size = pool->priv->entry->cur_dtors;
+ }
+
+ pool->priv->dtors_heap = rspamd_min_heap_create(reserved_size);
}
+ /* Allocate heap wrapper */
+ heap_wrapper = rspamd_mempool_alloc_(pool, sizeof(*heap_wrapper),
+ RSPAMD_ALIGNOF(struct _pool_destructor_heap_elt), line);
+ heap_wrapper->dtor = dtor;
+ heap_wrapper->heap_elt.data = heap_wrapper;
+ heap_wrapper->heap_elt.pri = priority;
+
+ rspamd_min_heap_push(pool->priv->dtors_heap, &heap_wrapper->heap_elt);
+
POOL_MTX_UNLOCK();
}
void *old_data,
void *new_data)
{
- struct _pool_destructors *tmp;
+ struct _pool_destructor_heap_elt *heap_wrapper;
+ struct _pool_destructors *dtor;
+ struct rspamd_min_heap_elt *elt;
+ gsize i, heap_size;
+
+ if (pool->priv->dtors_heap == NULL) {
+ return;
+ }
- LL_FOREACH(pool->priv->dtors_head, tmp)
- {
- if (tmp->func == func && tmp->data == old_data) {
- tmp->func = func;
- tmp->data = new_data;
+ /* Linear search through heap to find matching destructor */
+ heap_size = rspamd_min_heap_size(pool->priv->dtors_heap);
+ for (i = 0; i < heap_size; i++) {
+ elt = rspamd_min_heap_index(pool->priv->dtors_heap, i);
+ heap_wrapper = (struct _pool_destructor_heap_elt *) elt->data;
+ dtor = heap_wrapper->dtor;
+
+ if (dtor->func == func && dtor->data == old_data) {
+ dtor->data = new_data;
break;
}
}
void rspamd_mempool_destructors_enforce(rspamd_mempool_t *pool)
{
- struct _pool_destructors *destructor;
+ struct _pool_destructor_heap_elt *heap_wrapper;
+ struct _pool_destructors *dtor;
+ struct rspamd_min_heap_elt *elt;
POOL_MTX_LOCK();
- LL_FOREACH(pool->priv->dtors_head, destructor)
- {
- /* Avoid calling destructors for NULL pointers */
- if (destructor->data != NULL) {
- destructor->func(destructor->data);
+ if (pool->priv->dtors_heap) {
+ /* Pop destructors in priority order (min heap = lowest priority first) */
+ while ((elt = rspamd_min_heap_pop(pool->priv->dtors_heap)) != NULL) {
+ heap_wrapper = (struct _pool_destructor_heap_elt *) elt->data;
+ dtor = heap_wrapper->dtor;
+
+ /* Avoid calling destructors for NULL pointers */
+ if (dtor->data != NULL) {
+ dtor->func(dtor->data);
+ }
}
- }
- pool->priv->dtors_head = pool->priv->dtors_tail = NULL;
+ /* Destroy the heap itself */
+ rspamd_min_heap_destroy(pool->priv->dtors_heap);
+ pool->priv->dtors_heap = NULL;
+ }
rspamd_mempool_variables_cleanup(pool);
void rspamd_mempool_delete(rspamd_mempool_t *pool)
{
struct _pool_chain *cur, *tmp;
- struct _pool_destructors *destructor;
+ struct _pool_destructors *dtor;
+ struct rspamd_min_heap_elt *elt;
gpointer ptr;
unsigned int i;
gsize len;
GHashTable *debug_tbl = *(GHashTable **) (((unsigned char *) pool) + sizeof(*pool));
/* Show debug info */
gsize ndtor = 0;
- LL_COUNT(pool->priv->dtors_head, destructor, ndtor);
+
+ if (pool->priv->dtors_heap) {
+ ndtor = rspamd_min_heap_size(pool->priv->dtors_heap);
+ }
+
msg_info_pool("destructing of the memory pool %p; elt size = %z; "
"used memory = %Hz; wasted memory = %Hd; "
"vars = %z; destructors = %z",
}
}
- /* Call all pool destructors */
- LL_FOREACH(pool->priv->dtors_head, destructor)
- {
- /* Avoid calling destructors for NULL pointers */
- if (destructor->data != NULL) {
- destructor->func(destructor->data);
+ /* Call all pool destructors in priority order */
+ if (pool->priv->dtors_heap) {
+ struct _pool_destructor_heap_elt *heap_wrapper;
+ gsize ndtors = 0;
+
+ /* Update destructor statistics before destroying */
+ if (pool->priv->entry && mempool_entries) {
+ ndtors = rspamd_min_heap_size(pool->priv->dtors_heap);
+
+ /* Update suggestion using similar logic to variables */
+ if (pool->priv->entry->cur_dtors < ndtors) {
+ static const unsigned int max_preallocated_dtors = 128;
+
+ unsigned int old_guess = pool->priv->entry->cur_dtors;
+ unsigned int new_guess;
+
+ if (old_guess == 0) {
+ new_guess = MIN(ndtors, max_preallocated_dtors);
+ }
+ else {
+ if (old_guess * 2 < ndtors) {
+ new_guess = MIN(ndtors, max_preallocated_dtors);
+ }
+ else {
+ /* Too large step */
+ new_guess = MIN(old_guess * 2, max_preallocated_dtors);
+ }
+ }
+
+ pool->priv->entry->cur_dtors = new_guess;
+ }
}
+
+ while ((elt = rspamd_min_heap_pop(pool->priv->dtors_heap)) != NULL) {
+ heap_wrapper = (struct _pool_destructor_heap_elt *) elt->data;
+ dtor = heap_wrapper->dtor;
+
+ /* Avoid calling destructors for NULL pointers */
+ if (dtor->data != NULL) {
+ dtor->func(dtor->data);
+ }
+ }
+
+ rspamd_min_heap_destroy(pool->priv->dtors_heap);
+ pool->priv->dtors_heap = NULL;
}
rspamd_mempool_variables_cleanup(pool);
enum rspamd_mempool_flags {
RSPAMD_MEMPOOL_DEBUG = (1u << 0u),
+ RSPAMD_MEMPOOL_LONG_LIVED = (1u << 1u), /**< Pool for long-lived data (config, etc.) */
+ RSPAMD_MEMPOOL_SHORT_LIVED = (1u << 2u), /**< Pool for short-lived data (tasks, etc.) */
};
/**
#define rspamd_mempool_new_default(tag, flags) \
rspamd_mempool_new_(rspamd_mempool_suggest_size_(G_STRLOC), (tag), (flags), G_STRLOC)
+/* Convenient macros for creating typed pools */
+#define rspamd_mempool_new_long_lived(size, tag) \
+ rspamd_mempool_new_((size), (tag), RSPAMD_MEMPOOL_LONG_LIVED, G_STRLOC)
+#define rspamd_mempool_new_short_lived(tag) \
+ rspamd_mempool_new_(rspamd_mempool_suggest_size_(G_STRLOC), (tag), RSPAMD_MEMPOOL_SHORT_LIVED, G_STRLOC)
+
/**
* Get memory from pool
* @param pool memory pool object
MAX(MIN_MEM_ALIGNMENT, RSPAMD_ALIGNOF(type)), (G_STRLOC)))
/**
- * Add destructor callback to pool
+ * Add destructor callback to pool with priority
* @param pool memory pool object
* @param func pointer to function-destructor
* @param data pointer to data that would be passed to destructor
+ * @param function function name
+ * @param line line number
+ * @param priority destructor priority (lower values = earlier execution, 0 = default)
*/
void rspamd_mempool_add_destructor_full(rspamd_mempool_t *pool,
rspamd_mempool_destruct_t func,
void *data,
const char *function,
- const char *line);
+ const char *line,
+ unsigned int priority);
/* Macros for common usage */
#define rspamd_mempool_add_destructor(pool, func, data) \
- rspamd_mempool_add_destructor_full(pool, func, data, (MEMPOOL_STR_FUNC), (G_STRLOC))
+ rspamd_mempool_add_destructor_full(pool, func, data, (MEMPOOL_STR_FUNC), (G_STRLOC), 0)
+
+#define rspamd_mempool_add_destructor_priority(pool, func, data, priority) \
+ rspamd_mempool_add_destructor_full(pool, func, data, (MEMPOOL_STR_FUNC), (G_STRLOC), priority)
/**
* Replace destructor callback to pool for specified pointer
#ifndef RSPAMD_MEM_POOL_INTERNAL_H
#define RSPAMD_MEM_POOL_INTERNAL_H
+#include "config.h"
+#include "heap.h"
+
/*
* Internal memory pool stuff
*/
uint32_t cur_suggestion;
uint32_t cur_elts;
uint32_t cur_vars;
+ uint32_t cur_dtors; /**< suggested number of destructors to preallocate */
struct entry_elt elts[ENTRY_NELTS];
};
/**
- * Destructors list item structure
+ * Destructors heap item structure
*/
struct _pool_destructors {
- rspamd_mempool_destruct_t func; /**< pointer to destructor */
- void *data; /**< data to free */
+ rspamd_mempool_destruct_t func; /**< pointer to destructor */
+ void *data; /**< data to free */
const char *function; /**< function from which this destructor was added */
- const char *loc; /**< line number */
- struct _pool_destructors *next;
+ const char *loc; /**< line number */
+ unsigned int priority; /**< destructor priority (0 = default) */
};
+/**
+ * Wrapper for destructor with heap element
+ */
+struct _pool_destructor_heap_elt {
+ struct rspamd_min_heap_elt heap_elt;
+ struct _pool_destructors *dtor;
+};
struct rspamd_mempool_variable {
gpointer data;
struct rspamd_mempool_specific {
struct _pool_chain *pools[RSPAMD_MEMPOOL_MAX];
- struct _pool_destructors *dtors_head, *dtors_tail;
+ struct rspamd_min_heap *dtors_heap;
GPtrArray *trash_stack;
khash_t(rspamd_mempool_vars_hash) * variables;
struct rspamd_mempool_entry_point *entry;