#ifndef LL_QUEUE_H #define LL_QUEUE_H #include // для size_t #include "memory_pool.h" // для struct memory_pool // Предварительные объявления struct ll_queue; struct ll_entry; struct queue_waiter; struct memory_pool; // Тип коллбэка: вызывается при добавлении элемента в пустую очередь или для продолжения обработки // Параметры: указатель на очередь, указатель на элемент (первый в очереди), пользовательский аргумент typedef void (*queue_callback_fn)(struct ll_queue* q, struct ll_entry* entry, void* arg); // Структура элемента - переменный размер, данные расположены сразу после структуры struct ll_entry { struct ll_entry* next; // Указатель на следующий элемент в очереди size_t size; // Размер данных элемента (байт) int ref_count; // Счетчик ссылок для предотвращения double-free }; // Структура условия ожидания (waiter) struct queue_waiter { int max_packets; // Максимальное количество пакетов size_t max_bytes; // Максимальное количество байт 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 }; // ==================== Управление очередью ==================== // Создать новую пустую очередь // 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) { return (void*)(entry + 1); } // Получить размер данных элемента static inline size_t ll_entry_size(struct ll_entry* entry) { return entry->size; } // ==================== Асинхронное ожидание ==================== // Зарегистрировать одноразовый коллбэк, который будет вызван когда очередь будет иметь // не более max_packets пакетов и не более max_bytes байт. // Если условие уже выполнено, коллбэк вызывается немедленно. // Можно зарегистрировать несколько ожиданий на одной очереди. // Возвращает указатель на 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); #endif // LL_QUEUE_H