# memory_pool (Object Pool Allocator) ## 1. Назначение Быстрый аллокатор объектов фиксированного размера. Вместо постоянных `malloc`/`free` хранит до 64 освобождённых объектов в linked list для повторного использования. Применяется для hot-path объектов, которые часто создаются и уничтожаются: пакеты, фрагменты, ACK-пакеты, структуры маршрутизации, TCP-сегменты lwIP и т.д. Встроенная защита: - **Buffer overflow** — канарейка `0xDEADBEAF` после пользовательских данных проверяется при освобождении. При повреждении — бесконечный цикл (hang) для отладки. - **Double free** — однобайтный счётчик в метаданных; повторное освобождение детектируется и вызывает hang. - Вся диагностика пишется через `DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, ...)`. Не потокобезопасен — каждый пул используется из одного потока (обычно в рамках одного u_async event loop). ## 2. Как пользоваться ```c #include "memory_pool.h" // 1. Создать пул (обычно в init-функции инстанса): struct memory_pool* pkt_pool = memory_pool_init(sizeof(struct ETCP_DGRAM), "pkt_pool"); // 2. Выделить объект (если есть свободный — вернёт из пула, иначе u_calloc): struct ETCP_DGRAM* dgram = memory_pool_alloc(pkt_pool); // 3. Освободить объект (возвращается в пул, если там меньше 64 блоков): memory_pool_free(pkt_pool, dgram); // 4. Получить статистику: size_t allocs, reuse; memory_pool_get_stats(pkt_pool, &allocs, &reuse); // reuse много → эффективно; reuse мало → пул слишком мал для нагрузки // 5. Проверить, в пуле ли объект (0 — не в пуле, 1 — в пуле): if (memory_pool_is_freed(pkt_pool, obj)) { /* уже освобождён */ } // 6. Уничтожить пул (при shutdown): memory_pool_destroy(pkt_pool); // реально освобождает все кэшированные блоки через u_free() и сам пул. ``` **Важно:** - Если объекты пула используются как элементы `ll_queue`, в `object_size` нужно закладывать `sizeof(struct ll_entry)`. Например: `memory_pool_init(sizeof(struct ll_entry) + sizeof(struct dummynet_pkt), "pkt_pool")`. - `memory_pool_alloc` обнуляет объект перед возвратом (защита от утечки старых данных). - При заполнении пула (64 свободных блока) лишние `memory_pool_free` реально вызывают `u_free` — блок не кэшируется. - `memory_pool_destroy` проходит по всем свободным блокам и вызывает `u_free` для каждого, затем освобождает сам `struct memory_pool`. ## 3. API | Функция/макрос | Описание | |---|---| | `memory_pool_init(object_size, name)` | Создаёт пул для объектов заданного размера. `name` — для диагностики. | | `memory_pool_alloc(pool)` | Выделяет объект: из кэша (если есть) или через `u_calloc`. Обнуляет перед возвратом. | | `memory_pool_free(pool, obj)` | Возвращает объект в пул (до 64) или вызывает `u_free`. Проверяет canary и double-free. | | `memory_pool_destroy(pool)` | Уничтожает пул: освобождает все кэшированные блоки и сам пул. | | `memory_pool_get_stats(pool, &allocs, &reuse)` | Статистика: общее число аллокаций и число повторных использований из кэша. | | `memory_pool_get_total_free_blocks()` | Глобальный счётчик свободных блоков во всех пулах (для диагностики). | | `memory_pool_is_freed(pool, obj)` | Проверяет, находится ли объект в свободном списке пула (уже освобождён). |