# timeout_heap — Min-heap для управления таймерами ## 1. Назначение timeout_heap — легковесная реализация min-heap (двоичная куча с минимумом в корне) для хранения таймеров в порядке возрастания времени срабатывания. Используется модулем `u_async` как внутренняя структура `UASYNC::timeout_heap`. Ключевая особенность — **ленивое удаление (lazy deletion)**: при отмене таймера элемент не удаляется из кучи, а помечается флагом `deleted = 1`, его `expiration` обнуляется, и он всплывает в корень (bubble-up). Реальное удаление происходит при `peek`/`pop` — корень с `deleted == 1` выкидывается, а данные освобождаются через пользовательский `free_callback`. Это даёт O(log n) на отмену без полного удаления из середины кучи. ## 2. Как пользоваться Модуль используется только внутри `u_async.c`, пользователь напрямую с ним не работает. Приводится для понимания внутреннего устройства. ### Создание и уничтожение ```c TimeoutHeap* h = timeout_heap_create(16); // начальная ёмкость 16 timeout_heap_set_free_callback(h, ua, timeout_node_free_callback); // ... использование ... timeout_heap_destroy(h); // уничтожает все элементы через free_callback ``` ### Вставка и извлечение ```c size_t index; timeout_heap_push(h, expiration_ms, my_data, &index); // index обновляется при перемещениях элемента в куче TimeoutEntry entry; if (timeout_heap_peek(h, &entry) == 0) { // entry.expiration — ближайшее время срабатывания // entry.data — пользовательские данные } if (timeout_heap_pop(h, &entry) == 0) { // элемент удалён из кучи, данные нужно освободить вручную или через free_callback } ``` ### Отмена ```c // O(n) — линейный поиск по expiration+data: timeout_heap_cancel(h, expiration, data); // O(log n) — удаление по индексу (для внешнего отслеживания позиции): timeout_heap_cancel_at(h, index, data); ``` ### Ленивое удаление: как это работает 1. `cancel` / `cancel_at` ставит `deleted = 1`, `expiration = 0`, вызывает `bubble_up` — элемент всплывает в корень. 2. При следующем `peek`/`pop`: если корень `deleted`, он удаляется из кучи (`remove_root`), данные освобождаются через `free_callback`. Повторяется, пока корень не окажется активным. 3. O(log n) как на cancel, так и на последующее удаление. ## 3. API ### Жизненный цикл | Функция | Описание | |---------|----------| | `timeout_heap_create(capacity)` | Создать кучу с начальной ёмкостью. Расширяется автоматически при заполнении. | | `timeout_heap_destroy(h)` | Уничтожить кучу. Все данные (включая помеченные deleted) освобождаются через `free_callback` или `free()`. | | `timeout_heap_set_free_callback(h, user_data, cb)` | Установить callback для освобождения данных при удалении deleted-узлов. Если NULL — данные не освобождаются. | ### Основные операции | Функция | Описание | |---------|----------| | `timeout_heap_push(h, exp, data, &index)` | Вставить элемент с временем срабатывания `exp`. `index_ptr` (опционально) отслеживает позицию элемента в куче — обновляется при всех перемещениях. O(log n). | | `timeout_heap_peek(h, &entry)` | Посмотреть ближайший **неудалённый** элемент без удаления. Попутно вычищает deleted-корни через `free_callback`. O(log n) в худшем случае. | | `timeout_heap_pop(h, &entry)` | Извлечь ближайший неудалённый элемент. Аналогично чистит deleted. O(log n). | ### Отмена | Функция | Описание | |---------|----------| | `timeout_heap_cancel(h, exp, data)` | Отменить по совпадению `expiration + data`. Линейный поиск — O(n). Для массового использования предпочитать `cancel_at`. | | `timeout_heap_cancel_at(h, index, data)` | Отменить по индексу (с проверкой data). O(log n) — ленивое удаление с bubble-up. | ### Статистика | Функция | Описание | |---------|----------| | `timeout_heap_get_size(h)` | Текущий размер кучи (включая deleted-элементы). | | `timeout_heap_get_freed_count(h)` | Количество узлов, освобождённых через `free_callback` (не реализовано в текущей версии — возвращает 0). | ### Структуры ```c typedef struct { TimeoutTime expiration; // время срабатывания (ключ сортировки) void *data; // пользовательские данные size_t *index_ptr; // указатель на внешнюю переменную с индексом (обновляется при перемещениях) int deleted; // 0 = активен, 1 = помечен на удаление } TimeoutEntry; struct TimeoutHeap { TimeoutEntry *heap; // динамический массив size_t size; // текущее количество элементов size_t capacity; // выделенная ёмкость void* user_data; // аргумент для free_callback void (*free_callback)(void*, void*); // освобождение данных deleted-узлов }; ``` Индексация в коде: внешний API использует 0-based индексы в массиве `heap[]`. Внутренние операции (`bubble_up`, `heapify_down`) оперируют 1-based индексами через макросы `PARENT(i)`, `LEFT_CHILD(i)`, `RIGHT_CHILD(i)` для упрощения арифметики кучи.