diff --git a/lib/timeout_heap.h b/lib/timeout_heap.h index be1c5cd6..9a7e1b92 100644 --- a/lib/timeout_heap.h +++ b/lib/timeout_heap.h @@ -3,7 +3,12 @@ * cancel помечает запись; её data передаётся free_callback при последующей очистке или destroy. * Без free_callback data не освобождается. Успешный pop передаёт data вызывающему. * index_ptr, если задан, должен жить до удаления записи; heap обновляет индекс при перестановках. - * Все операции одного heap выполняются в одном потоке, внутренней синхронизации нет. */ + * Все операции одного heap выполняются в одном потоке, внутренней синхронизации нет. + * Внешние индексы 0-based; после удаления их значение больше не определяет запись и не сбрасывается автоматически. + * push/cancel_at — O(log n), cancel с поиском — O(n). Отмена ставит deleted=1, expiration=0 и поднимает запись. + * peek/pop очищают k отменённых корней за O(k log n), вызывая free_callback; успешный pop его не вызывает. + * Равные expiration не гарантируют FIFO. Размер включает ещё не очищенные отменённые записи. + * Пример: create → set_free_callback → push(expiration, data, &живой_индекс) → peek/pop или cancel_at → destroy. */ #ifndef TIMEOUT_HEAP_H #define TIMEOUT_HEAP_H diff --git a/lib/timeout_heap_doc.md b/lib/timeout_heap_doc.md deleted file mode 100644 index a52e88e6..00000000 --- a/lib/timeout_heap_doc.md +++ /dev/null @@ -1,107 +0,0 @@ -# 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)` для упрощения арифметики кучи.