Clarify queue callback scheduling and ownership contracts
This commit is contained in:
1 parent
9079ab5c24
commit
dffcd19d21
1 file changed
+57
-24
+57
-24
@@ -75,7 +75,7 @@ struct ll_entry {
|
||||
uint8_t* dgram; // Указатель на данные пакета
|
||||
void (*dgram_free_fn)(uint8_t* data); // Кастомная функция освобождения dgram
|
||||
struct memory_pool* dgram_pool; // Пул для dgram (если используется)
|
||||
uint32_t index_hash; // Хеш по первым 4 байтам индекса
|
||||
uint32_t index_hash; // FNV-1a (32 бита) по всем index_size байтам data[] от index_offset
|
||||
struct ll_entry* hash_next; // Следующий в хеш-цепочке
|
||||
struct memory_pool* pool; // Пул, из которого выделен сам entry (NULL = malloc)
|
||||
uint8_t data[0]; // Гибкий массив пользовательских данных (размер = size)
|
||||
@@ -219,19 +219,24 @@ void queue_set_size_limit(struct ll_queue* q, int lim);
|
||||
/**
|
||||
* @brief Устанавливает коллбэк для автоматического извлечения элементов.
|
||||
* @param q очередь
|
||||
* @param cbk_fn функция-коллбэк
|
||||
* @param cbk_fn функция-коллбэк (NULL отключает автозабор)
|
||||
* @param arg пользовательский аргумент
|
||||
*
|
||||
* Коллбэк вызывается автоматически при наличии элементов и разрешённом состоянии.
|
||||
* Только заменяет функцию и аргумент: не запускает обработку, не снимает callback_suspended
|
||||
* и не отменяет уже запланированный resume. Отложенный resume использует новые функцию и аргумент.
|
||||
* Для запуска автозабора из уже заполненной очереди вызовите queue_resume_callback().
|
||||
*/
|
||||
void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Возобновляет работу коллбэка после обработки элемента в следующем event loop.
|
||||
* @brief Разрешает автозабор и планирует отложенный вызов коллбэка.
|
||||
* @param q очередь
|
||||
*
|
||||
* @warning ОБЯЗАТЕЛЬНО вызывать после завершения обработки элемента в коллбэке.
|
||||
* Иначе автозабор элементов навсегда остановится.
|
||||
* Сразу снимает callback_suspended. Если есть элементы и callback, планирует uasync_call_soon,
|
||||
* если resume ещё не запланирован. При исполнении повторно проверяет наличие элементов,
|
||||
* callback и отсутствие приостановки. Последующий put в пустую очередь может вызвать callback
|
||||
* синхронно, если callback_defer == 0.
|
||||
* @note Вызывать после завершения обработки извлечённого элемента для продолжения автозабора.
|
||||
*/
|
||||
void queue_resume_callback(struct ll_queue* q);
|
||||
|
||||
@@ -268,8 +273,11 @@ void queue_set_callback_defer(struct ll_queue* q, int enable);
|
||||
* @param cbk_fn коллбэк (NULL для отмены)
|
||||
* @param arg пользовательский аргумент
|
||||
*
|
||||
* Коллбэк вызывается асинхронно (через uasync_call_soon) когда count становится 0.
|
||||
* После вызова сбрасывается — для повторного использования нужно вызвать снова.
|
||||
* Заменяет регистрацию и отменяет её прежний отложенный вызов. При count == 0 сразу планирует
|
||||
* uasync_call_soon; иначе вызов планируется при опустошении через queue_data_get/queue_remove_data.
|
||||
* При исполнении снова проверяет count: если очередь заполнилась, регистрация сохраняется
|
||||
* до следующего опустошения. Функция и аргумент сбрасываются ДО вызова callback,
|
||||
* поэтому внутри него можно зарегистрировать новое уведомление.
|
||||
*/
|
||||
void queue_set_empty_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg);
|
||||
|
||||
@@ -284,12 +292,16 @@ void queue_set_on_get(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg);
|
||||
/**
|
||||
* @brief Регистрирует ожидание освобождения очереди до общего порога.
|
||||
* @param q очередь
|
||||
* @param h полностью обнулённый handle: `struct queue_waiter_handle h = {0}`
|
||||
* @param h handle, полностью обнулённый перед первым использованием: `struct queue_waiter_handle h = {0}`
|
||||
* @param callback функция, вызываемая при достижении порога
|
||||
* @param arg аргумент коллбэка
|
||||
* @return 1 — условие выполнено (callback вызван или отложен), 0 — ожидает порог, -1 — ошибка
|
||||
*
|
||||
* Если handle уже ожидает или имеет отложенный callback — старое ожидание отменяется.
|
||||
* При корректных аргументах повторная регистрация в той же очереди отменяет прежнее ожидание.
|
||||
* Если h->internal != NULL, перед переносом в другую очередь обязательно вызовите
|
||||
* queue_waiter_cancel(old_q, h): очередь старого ожидания в handle не хранится.
|
||||
* Уже отложенный callback (h->call_soon_id != NULL) отменяется по сохранённому h->defer_q,
|
||||
* поэтому в этом состоянии handle можно передать другой очереди без отдельной отмены.
|
||||
* При queue_free отменяются также уже запланированные уведомления.
|
||||
*/
|
||||
int queue_waiter_wait(struct ll_queue* q, struct queue_waiter_handle* h,
|
||||
@@ -297,44 +309,61 @@ int queue_waiter_wait(struct ll_queue* q, struct queue_waiter_handle* h,
|
||||
|
||||
/**
|
||||
* @brief Отменяет ожидание (удаляет из списка, если зарегистрирован).
|
||||
* @param q очередь
|
||||
* @param q очередь исходного ожидания (для h->internal); не NULL
|
||||
* @param h указатель на handle
|
||||
*
|
||||
* Безопасно вызывать в любом состоянии: если waiter не зарегистрирован — ничего не делает.
|
||||
* Отложенный callback отменяется через сохранённую h->defer_q. Если ожидания и отложенного
|
||||
* callback нет — ничего не делает. При h->internal != NULL передавать другую очередь нельзя.
|
||||
*/
|
||||
void queue_waiter_cancel(struct ll_queue* q, struct queue_waiter_handle* h);
|
||||
|
||||
// ==================== Работа с данными ====================
|
||||
|
||||
/**
|
||||
* Общие правила владения для всех четырёх queue_data_put*:
|
||||
* - При q == NULL или entry == NULL возвращается -1 без освобождения памяти.
|
||||
* - Элемент должен быть извлечён из прежней очереди: owner, prev и next должны быть NULL.
|
||||
* Повторная вставка связанного элемента приводит к abort().
|
||||
* - При остальных отказах (лимит, неверный вид put или границы индекса) освобождаются
|
||||
* сначала dgram через queue_dgram_free(), затем entry через queue_entry_free().
|
||||
* - При успехе владение передаётся очереди/потребителю. Put может синхронно вызвать callback,
|
||||
* который обработает и освободит entry и даже очередь до возврата из put.
|
||||
* Использовать эти указатели после put можно только при гарантированном времени жизни.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Добавляет элемент в конец очереди (FIFO).
|
||||
* @param q очередь
|
||||
* @param entry элемент
|
||||
* @return 0 — успех, -1 — превышен лимит (элемент освобождён)
|
||||
* @param q очередь без хеш-таблицы (hash_size == 0)
|
||||
* @param entry элемент; правила передачи владения см. выше
|
||||
* @return 0 — добавлен, -1 — NULL-аргумент, переполнение или hash_size > 0
|
||||
*/
|
||||
int queue_data_put(struct ll_queue* q, struct ll_entry* entry);
|
||||
|
||||
/**
|
||||
* @brief Добавляет элемент в начало очереди (LIFO, высокий приоритет).
|
||||
* @param q очередь
|
||||
* @param entry элемент
|
||||
* @return 0 — успех, -1 — превышен лимит (элемент освобождён)
|
||||
* @param q очередь без хеш-таблицы (hash_size == 0)
|
||||
* @param entry элемент; правила передачи владения см. выше
|
||||
* @return 0 — добавлен, -1 — NULL-аргумент, переполнение или hash_size > 0
|
||||
*/
|
||||
int queue_data_put_first(struct ll_queue* q, struct ll_entry* entry);
|
||||
|
||||
/**
|
||||
* @brief Добавляет элемент в конец очереди с хеш-индексом (использует index_offset/index_size из очереди).
|
||||
* @param q очередь
|
||||
* @param entry элемент
|
||||
* @return 0 — успех, -1 — ошибка
|
||||
* @param q очередь с настроенной хеш-таблицей (hash_size > 0, index_size > 0)
|
||||
* @param entry элемент; ключ занимает index_size байт в data[] от index_offset и должен помещаться в entry->size
|
||||
* @return 0 — добавлен, -1 — NULL-аргумент, переполнение или неверные границы/нулевой размер индекса
|
||||
* @note Владение см. выше. При hash_size == 0 реализация может вернуть 0, добавив элемент без индексации;
|
||||
* наличие хеш-таблицы — предусловие, а не гарантированная проверка с возвратом -1.
|
||||
*/
|
||||
int queue_data_put_with_index(struct ll_queue* q, struct ll_entry* entry);
|
||||
|
||||
/**
|
||||
* @brief Добавляет элемент в начало очереди с хеш-индексом (использует index_offset/index_size из очереди).
|
||||
* @param q очередь
|
||||
* @param entry элемент
|
||||
* @return 0 — успех, -1 — ошибка
|
||||
* @param q очередь с настроенной хеш-таблицей (hash_size > 0, index_size > 0)
|
||||
* @param entry элемент; ключ занимает index_size байт в data[] от index_offset и должен помещаться в entry->size
|
||||
* @return 0 — добавлен, -1 — NULL-аргумент, переполнение или неверные границы/нулевой размер индекса
|
||||
* @note Владение см. выше. При hash_size == 0 реализация может вернуть 0, добавив элемент без индексации;
|
||||
* наличие хеш-таблицы — предусловие, а не гарантированная проверка с возвратом -1.
|
||||
*/
|
||||
int queue_data_put_first_with_index(struct ll_queue* q, struct ll_entry* entry);
|
||||
|
||||
@@ -393,6 +422,10 @@ void queue_entry_free(struct ll_entry* entry);
|
||||
/**
|
||||
* @brief Освобождает буфер dgram элемента.
|
||||
* @param entry элемент
|
||||
*
|
||||
* При dgram != NULL вызывает dgram_free_fn, если она задана; иначе возвращает буфер в dgram_pool,
|
||||
* если задан пул; иначе вызывает u_free. Затем сбрасывает dgram и len. Сам entry не освобождает.
|
||||
* Внешнему буферу нужен подходящий dgram_free_fn.
|
||||
*/
|
||||
void queue_dgram_free(struct ll_entry* entry);
|
||||
|
||||
|
||||
Reference in new issue
Block a user