You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6.9 KiB

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, пользователь напрямую с ним не работает. Приводится для понимания внутреннего устройства.

Создание и уничтожение

TimeoutHeap* h = timeout_heap_create(16);  // начальная ёмкость 16
timeout_heap_set_free_callback(h, ua, timeout_node_free_callback);
// ... использование ...
timeout_heap_destroy(h);  // уничтожает все элементы через free_callback

Вставка и извлечение

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
}

Отмена

// 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).

Структуры

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) для упрощения арифметики кучи.