Consolidate timeout heap ownership and complexity documentation
This commit is contained in:
1 parent
af3c7a4217
commit
ace9cb70d0
2 files changed
+6
-108
No files matched your search
+6
-1
@@ -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
|
||||
|
||||
@@ -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)` для упрощения арифметики кучи.
|
||||
Reference in new issue
Block a user