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.
224 lines
13 KiB
224 lines
13 KiB
// ll_queue.h |
|
#ifndef LL_QUEUE_H |
|
#define LL_QUEUE_H |
|
|
|
#include <stddef.h> // для size_t |
|
#include <stdint.h> // для uint64_t |
|
#include "memory_pool.h" // для struct memory_pool |
|
|
|
// Предварительные объявления |
|
struct ll_queue; |
|
struct ll_entry; |
|
struct queue_waiter; |
|
struct memory_pool; |
|
struct ll_queue_pos; // Управляющая структура позиции |
|
|
|
// Тип коллбэка: вызывается при добавлении элемента в пустую очередь или для продолжения обработки |
|
// Параметры: указатель на очередь, указатель на элемент (первый в очереди), пользовательский аргумент |
|
typedef void (*queue_callback_fn)(struct ll_queue* q, struct ll_entry* entry, void* arg); |
|
|
|
// Структура элемента - переменный размер, данные расположены сразу после структуры |
|
struct ll_entry { |
|
struct ll_entry* next; // Указатель на следующий элемент в очереди |
|
struct ll_entry* prev; // Указатель на предыдущий элемент в очереди |
|
size_t size; // Размер данных элемента (байт) |
|
int ref_count; // Счетчик ссылок для предотвращения double-free |
|
}; |
|
|
|
// Структура условия ожидания (waiter) |
|
struct queue_waiter { |
|
int max_packets; // Максимальное количество пакетов |
|
size_t max_bytes; // Максимальное количество байт (0 = не проверять байты) |
|
void (*callback)(struct ll_queue* q, void* arg); // Коллбэк для вызова |
|
void* callback_arg; // Аргумент коллбэка |
|
struct queue_waiter* next; // Следующий ожидающий в списке |
|
}; |
|
|
|
typedef void (*queue_threshold_callback_fn)(struct ll_queue* q, void* arg); |
|
|
|
// Структура очереди |
|
struct ll_queue { |
|
struct ll_entry* head; // Первый элемент (извлекается отсюда) |
|
struct ll_entry* tail; // Последний элемент (добавляется сюда) |
|
int count; // Текущее количество элементов |
|
size_t total_bytes; // Общий размер данных всех элементов (байт) |
|
int size_limit; // Максимальное количество (-1 = без ограничения) |
|
|
|
queue_callback_fn callback; // Функция коллбэка |
|
void* callback_arg; // Пользовательский аргумент для коллбэка |
|
int callback_suspended; // 1 если коллбэки приостановлены (во время обработки) |
|
|
|
void* resume_timeout_id; // ID таймаута uasync для отложенного возобновления |
|
struct UASYNC* ua; // Экземпляр uasync для таймеров |
|
|
|
struct queue_waiter* waiters; // Список ожидающих коллбэков |
|
|
|
// Пулы памяти для оптимизации аллокаций |
|
struct memory_pool* pool; // Пул для структур struct queue_waiter |
|
|
|
uint64_t version; // Версия для инвалидации позиций |
|
}; |
|
|
|
// ==================== Управление очередью ==================== |
|
|
|
// Создать новую пустую очередь |
|
// ua - экземпляр uasync для таймеров (обязательный параметр) |
|
// pool - если не null то использовать этот пул памяти |
|
// Возвращает: указатель на очередь или NULL при ошибке выделения памяти (see memory_pool.c/h) |
|
struct ll_queue* queue_new(struct UASYNC* ua, struct memory_pool* pool); |
|
|
|
// Освободить очередь и все её элементы |
|
// Также отменяет отложенное возобновление если оно запланировано |
|
void queue_free(struct ll_queue* q); |
|
|
|
// ==================== Конфигурация очереди ==================== |
|
|
|
// Установить функцию и аргумент коллбэка для автозабора из очереди |
|
// Коллбэк вызывается когда в очереди есть элемент и разрешен коллбэк |
|
// обработчик должен обработать этот пакет (может использовать асинхронное ожидание). Когда будет готов к приёму следующего - должен вызвать resume_callback. обработка строго по одному пакету. |
|
void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
|
|
|
// Возобновить коллбэки после обработки элемента переданного в коллбэке (тянуть дополнительные элементы из очереди не предусмотернные api нельзя). |
|
// эта функция должна вызываться всегда после того как cbk_fn обработала пакет (можно с ожиданием через async), иначе очередь застрянет. |
|
// Если в очереди остались элементы, запланирует вызов коллбэка через uasync_set_timeout(0) |
|
// Это предотвращает накопление рекурсии в стеке вызовов |
|
void queue_resume_callback(struct ll_queue* q); |
|
|
|
// Установить максимальное количество элементов в очереди |
|
// При превышении лимита новый элемент автоматически освобождается |
|
void queue_set_size_limit(struct ll_queue* q, int lim); |
|
|
|
// ==================== Управление элементами ==================== |
|
|
|
// Создать новый элемент с областью данных указанного размера |
|
// Память выделяется одним блоком: [struct ll_entry][область данных data_size байт] |
|
// Возвращает: указатель на элемент или NULL при ошибке выделения памяти |
|
struct ll_entry* queue_entry_new(size_t data_size); |
|
|
|
// Освободить элемент (не влияет на связи в очереди) |
|
void queue_entry_free(struct ll_entry* entry); |
|
|
|
// ==================== Операции с очередью ==================== |
|
|
|
// Добавить элемент в конец очереди (FIFO) |
|
// Если очередь была пустой и коллбэки разрешены - вызывает коллбэк |
|
// Возвращает: 0 при успехе, -1 если превышен лимит размера (элемент освобожден) |
|
int queue_entry_put(struct ll_queue* q, struct ll_entry* entry); |
|
|
|
// Добавить элемент в начало очереди (LIFO, высокий приоритет) |
|
// Если очередь была пустой и коллбэки разрешены - вызывает коллбэк |
|
// Возвращает: 0 при успехе, -1 если превышен лимит размера (элемент освобожден) |
|
int queue_entry_put_first(struct ll_queue* q, struct ll_entry* entry); |
|
|
|
// Извлечь элемент из начала очереди |
|
// При извлечении приостанавливает коллбэки (callback_suspended = 1) чтобы предотвратить рекурсию |
|
// Возвращает: указатель на элемент или NULL если очередь пуста |
|
struct ll_entry* queue_entry_get(struct ll_queue* q); |
|
|
|
// Получить текущее количество элементов в очереди |
|
int queue_entry_count(struct ll_queue* q); |
|
|
|
// ==================== Вспомогательные функции ==================== |
|
|
|
// Получить указатель на область данных элемента |
|
// Данные расположены сразу после структуры struct ll_entry |
|
static inline void* ll_entry_data(struct ll_entry* entry) { |
|
if (!entry) return NULL; |
|
return (void*)(entry + 1); |
|
} |
|
|
|
// Получить размер данных элемента |
|
static inline size_t ll_entry_size(struct ll_entry* entry) { |
|
if (!entry) return 0; |
|
return entry->size; |
|
} |
|
|
|
// ==================== Асинхронное ожидание ==================== |
|
|
|
// Зарегистрировать одноразовый коллбэк, который будет вызван когда очередь будет иметь |
|
// не более max_packets пакетов и не более max_bytes байт (если max_bytes != 0). |
|
// Если условие уже выполнено, коллбэк вызывается немедленно. |
|
// Можно зарегистрировать несколько ожиданий на одной очереди. |
|
// Возвращает указатель на waiter для возможной отмены через queue_cancel_wait |
|
struct queue_waiter* queue_wait_threshold(struct ll_queue* q, int max_packets, size_t max_bytes, |
|
queue_threshold_callback_fn callback, void* arg); |
|
|
|
// Отменить ожидание (удалить waiter из списка) |
|
void queue_cancel_wait(struct ll_queue* q, struct queue_waiter* waiter); |
|
|
|
// Получить общий размер данных в очереди (байт) |
|
static inline size_t queue_total_bytes(struct ll_queue* q) { |
|
if (!q) return 0; |
|
return q->total_bytes; |
|
} |
|
|
|
// Получить статистику использования пулов памяти |
|
void queue_get_pool_stats(struct ll_queue* q, size_t* waiter_allocations, size_t* waiter_reuse); |
|
|
|
// ==================== Управление позициями ==================== |
|
|
|
// Структура управления позицией для навигации по очереди |
|
struct ll_queue_pos { |
|
struct ll_queue* queue; // Ссылка на очередь |
|
struct ll_entry* current; // Текущий элемент (NULL = перед head) |
|
struct ll_entry* prev; // Предыдущий элемент (для навигации) |
|
int index; // Текущий индекс (-1 = перед head) |
|
int valid; // Флаг валидности позиции |
|
uint64_t version; // Версия очереди на момент создания/обновления позиции |
|
}; |
|
|
|
// Создать новую позицию для очереди |
|
struct ll_queue_pos* queue_pos_new(struct ll_queue* queue); |
|
|
|
// Освободить позицию (не удаляет элементы очереди) |
|
void queue_pos_free(struct ll_queue_pos* pos); |
|
|
|
// Сбросить позицию в начальное состояние (перед head) |
|
void queue_pos_reset(struct ll_queue_pos* pos); |
|
|
|
// Перейти к следующему элементу |
|
// Возвращает: 1 при успехе, 0 если достигнут конец |
|
int queue_pos_next(struct ll_queue_pos* pos); |
|
|
|
// Перейти к предыдущему элементу |
|
// Возвращает: 1 при успехе, 0 если достигнуто начало |
|
int queue_pos_prev(struct ll_queue_pos* pos); |
|
|
|
// Перейти к конкретному индексу (0 = head) |
|
// Возвращает: 1 при успехе, 0 если индекс неверен |
|
int queue_pos_seek(struct ll_queue_pos* pos, int index); |
|
|
|
// Получить текущий элемент без удаления |
|
static inline struct ll_entry* queue_pos_current(struct ll_queue_pos* pos) { |
|
if (!pos || !pos->valid) return NULL; |
|
return pos->current; |
|
} |
|
|
|
// Получить текущий индекс |
|
static inline int queue_pos_index(struct ll_queue_pos* pos) { |
|
if (!pos || !pos->valid) return -1; |
|
return pos->index; |
|
} |
|
|
|
// Обновить позицию после изменения очереди (сбросить версию) |
|
static inline void queue_pos_refresh(struct ll_queue_pos* pos) { |
|
if (!pos || !pos->queue) return; |
|
pos->version = pos->queue->version; |
|
pos->valid = 1; |
|
} |
|
|
|
// Вставить элемент относительно текущей позиции |
|
// mode: 0 = перед current, 1 = после current |
|
// Возвращает: 0 при успехе, -1 при ошибке |
|
int queue_pos_insert(struct ll_queue_pos* pos, struct ll_entry* entry, int mode); |
|
|
|
// Удалить элемент из текущей позиции и вернуть его |
|
// После удаления current указывает на следующий элемент (или NULL) |
|
// Возвращает: указатель на удаленный элемент или NULL |
|
struct ll_entry* queue_pos_remove(struct ll_queue_pos* pos); |
|
|
|
// Удалить конкретный элемент из очереди по указателю |
|
// Возвращает: 0 при успехе, -1 если элемент не найден |
|
int queue_entry_remove(struct ll_queue* q, struct ll_entry* entry); |
|
|
|
#endif // LL_QUEUE_H |