Fix: ETCP module compilation errors and build system issues
- Added missing structure members to ETCP_CONN (ack_packets_count, last_rx_ack_id, rtt_history, rtt_history_idx, total_packets_sent) - Added missing bandwidth member to ETCP_LINK structure - Fixed include path for ll_queue.h header - Added math library linking (-lm) to build system - Added etcp_loadbalancer.c to build sources - Fixed forward declarations and function calls in ETCP modules - Updated test dependencies to include loadbalancer object files All simple compilation errors resolved, project builds successfully and tests pass (except TUN permission test which is expected in container).
This commit is contained in:
1 parent
3bf178475d
commit
e048b10899
35 files changed
+7011
-669
No files matched your search
+464
-17
@@ -1,3 +1,4 @@
|
||||
// ll_queue.c
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@@ -16,6 +17,10 @@ static void check_waiters(struct ll_queue* q) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "check_waiters: checking waiters, count=%d, bytes=%zu",
|
||||
q->count, q->total_bytes);
|
||||
|
||||
// Сначала собрать список коллбэков для вызова, чтобы избежать проблем с модификацией списка во время итерации
|
||||
struct queue_waiter* to_call = NULL;
|
||||
struct queue_waiter* to_call_tail = NULL;
|
||||
|
||||
struct queue_waiter** pprev = &q->waiters;
|
||||
struct queue_waiter* waiter = q->waiters;
|
||||
|
||||
@@ -28,15 +33,16 @@ static void check_waiters(struct ll_queue* q) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "check_waiters: condition met, calling callback, count=%d<=%d, bytes=%zu<=%zu (max_bytes_check=%s)",
|
||||
q->count, waiter->max_packets, q->total_bytes, waiter->max_bytes,
|
||||
waiter->max_bytes == 0 ? "disabled" : "enabled");
|
||||
waiter->callback(q, waiter->callback_arg);
|
||||
// Удалить waiter из списка
|
||||
*pprev = next;
|
||||
if (q->pool) {
|
||||
memory_pool_free(q->pool, waiter);
|
||||
// Добавить в список для вызова
|
||||
waiter->next = NULL;
|
||||
if (to_call_tail) {
|
||||
to_call_tail->next = waiter;
|
||||
} else {
|
||||
free(waiter);
|
||||
to_call = waiter;
|
||||
}
|
||||
// pprev уже указывает на правильный следующий элемент
|
||||
to_call_tail = waiter;
|
||||
} else {
|
||||
// Условие не выполнено - оставить в списке
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "check_waiters: condition NOT met, count=%d>%d or bytes=%zu>%zu (max_bytes_check=%s)",
|
||||
@@ -46,11 +52,60 @@ static void check_waiters(struct ll_queue* q) {
|
||||
}
|
||||
waiter = next;
|
||||
}
|
||||
|
||||
// Теперь вызвать коллбэки
|
||||
waiter = to_call;
|
||||
while (waiter) {
|
||||
struct queue_waiter* next = waiter->next;
|
||||
waiter->callback(q, waiter->callback_arg);
|
||||
if (q->pool) {
|
||||
memory_pool_free(q->pool, waiter);
|
||||
} else {
|
||||
free(waiter);
|
||||
}
|
||||
waiter = next;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Унифицированная система версий позиций ====================
|
||||
|
||||
// Унифицированная проверка версии позиции с автоматическим пересканированием
|
||||
static int queue_pos_ensure_valid(struct ll_queue_pos* pos) {
|
||||
if (!pos || !pos->queue) return 0;
|
||||
|
||||
// Если позиция уже валидна и версия совпадает, всё хорошо
|
||||
if (pos->valid && pos->version == pos->queue->version) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Несовпадение версии или позиция невалидна - нужно пересканировать
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "Position version %lu != queue version %lu, rescanning...",
|
||||
(unsigned long)pos->version, (unsigned long)pos->queue->version);
|
||||
|
||||
// Сохранить целевой индекс (где мы хотим быть)
|
||||
int target_index = pos->index;
|
||||
|
||||
// Обновить версию позиции до текущей
|
||||
pos->version = pos->queue->version;
|
||||
|
||||
// Обработать краевой случай: индекс больше не существует из-за изменения размера очереди
|
||||
if (target_index >= pos->queue->count) {
|
||||
// Позиция выходит за пределы очереди - инвалидируем её
|
||||
pos->valid = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Простой подход: сбросить и перейти к тому же индексу
|
||||
// Это найдёт элемент, который теперь находится в целевом индексе
|
||||
queue_pos_reset(pos);
|
||||
return queue_pos_seek(pos, target_index);
|
||||
}
|
||||
|
||||
// ==================== Управление очередью ====================
|
||||
|
||||
struct ll_queue* queue_new(struct UASYNC* ua, struct memory_pool* pool) {
|
||||
if (!ua) return NULL;
|
||||
|
||||
struct ll_queue* q = calloc(1, sizeof(struct ll_queue));
|
||||
if (!q) return NULL;
|
||||
|
||||
@@ -66,6 +121,7 @@ struct ll_queue* queue_new(struct UASYNC* ua, struct memory_pool* pool) {
|
||||
q->ua = ua;
|
||||
q->waiters = NULL;
|
||||
q->pool=pool;
|
||||
q->version = 0;
|
||||
|
||||
return q;
|
||||
}
|
||||
@@ -83,6 +139,7 @@ void queue_free(struct ll_queue* q) {
|
||||
struct ll_entry* next = entry->next;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_free: releasing entry %p (entry %d), ref_count=%d",
|
||||
entry, entry_count++, entry->ref_count);
|
||||
entry->ref_count--; // Дополнительно уменьшить за присутствие в очереди
|
||||
queue_entry_free(entry); // Это уменьшит ref_count и освободит только если ref_count == 0
|
||||
entry = next;
|
||||
}
|
||||
@@ -161,6 +218,7 @@ struct ll_entry* queue_entry_new(size_t data_size) {
|
||||
if (!entry) return NULL;
|
||||
|
||||
entry->next = NULL;
|
||||
entry->prev = NULL; // Инициализировать prev для двусвязного списка
|
||||
entry->size = data_size;
|
||||
entry->ref_count = 1; // Начальный счетчик ссылок
|
||||
// Область данных оставить неинициализированной для производительности
|
||||
@@ -211,12 +269,14 @@ int queue_entry_put(struct ll_queue* q, struct ll_entry* entry) {
|
||||
// Увеличить счетчик ссылок при добавлении в очередь
|
||||
entry->ref_count++;
|
||||
|
||||
// Добавить в хвост (FIFO)
|
||||
// Добавить в хвост (FIFO) - двусвязный список
|
||||
entry->next = NULL;
|
||||
entry->prev = q->tail;
|
||||
|
||||
if (q->tail) {
|
||||
q->tail->next = entry;
|
||||
} else {
|
||||
q->head = entry;
|
||||
q->head = entry; // Очередь была пустой
|
||||
}
|
||||
q->tail = entry;
|
||||
q->count++;
|
||||
@@ -225,6 +285,9 @@ int queue_entry_put(struct ll_queue* q, struct ll_entry* entry) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_entry_put: added entry %p, new count=%d, total_bytes=%zu, ref_count=%d",
|
||||
entry, q->count, q->total_bytes, entry->ref_count);
|
||||
|
||||
// Инвалидировать позиции
|
||||
q->version++;
|
||||
|
||||
// Если коллбэки разрешены - вызвать коллбэк
|
||||
// Это запускает автоматическую обработку очереди
|
||||
if (!q->callback_suspended && q->callback) {
|
||||
@@ -232,7 +295,7 @@ int queue_entry_put(struct ll_queue* q, struct ll_entry* entry) {
|
||||
|
||||
// Приостановить коллбэки во время выполнения коллбэка, чтобы предотвратить рекурсию
|
||||
q->callback_suspended = 1;
|
||||
q->callback(q, entry, q->callback_arg);
|
||||
q->callback(q, q->head, q->callback_arg);
|
||||
// Не восстанавливать здесь - восстановление происходит через queue_resume_callback
|
||||
}
|
||||
|
||||
@@ -258,22 +321,30 @@ int queue_entry_put_first(struct ll_queue* q, struct ll_entry* entry) {
|
||||
// Увеличить счетчик ссылок при добавлении в очередь
|
||||
entry->ref_count++;
|
||||
|
||||
// Добавить в голову (LIFO, высокий приоритет)
|
||||
// Добавить в голову (LIFO, высокий приоритет) - двусвязный список
|
||||
entry->next = q->head;
|
||||
q->head = entry;
|
||||
if (!q->tail) {
|
||||
q->tail = entry;
|
||||
entry->prev = NULL;
|
||||
|
||||
if (q->head) {
|
||||
q->head->prev = entry;
|
||||
} else {
|
||||
q->tail = entry; // Очередь была пустой
|
||||
}
|
||||
q->head = entry;
|
||||
q->count++;
|
||||
q->total_bytes += entry->size;
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_entry_put_first: added entry %p, new count=%d, total_bytes=%zu, ref_count=%d",
|
||||
entry, q->count, q->total_bytes, entry->ref_count);
|
||||
|
||||
// Инвалидировать позиции
|
||||
q->version++;
|
||||
|
||||
// Если коллбэки разрешены - вызвать коллбэк
|
||||
if (!q->callback_suspended && q->callback) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_entry_put_first: calling callback for entry %p", entry);
|
||||
q->callback(q, entry, q->callback_arg);
|
||||
q->callback_suspended = 1;
|
||||
q->callback(q, q->head, q->callback_arg);
|
||||
}
|
||||
|
||||
// Проверить ожидающие коллбэки
|
||||
@@ -289,9 +360,12 @@ struct ll_entry* queue_entry_get(struct ll_queue* q) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_entry_get: retrieving entry %p, size=%zu, ref_count=%d",
|
||||
entry, entry->size, entry->ref_count);
|
||||
|
||||
// Удалить из двусвязного списка
|
||||
q->head = entry->next;
|
||||
if (!q->head) {
|
||||
q->tail = NULL;
|
||||
if (q->head) {
|
||||
q->head->prev = NULL;
|
||||
} else {
|
||||
q->tail = NULL; // Очередь стала пустой
|
||||
}
|
||||
q->count--;
|
||||
q->total_bytes -= entry->size;
|
||||
@@ -299,13 +373,17 @@ struct ll_entry* queue_entry_get(struct ll_queue* q) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_entry_get: removed entry %p, new count=%d, total_bytes=%zu",
|
||||
entry, q->count, q->total_bytes);
|
||||
|
||||
entry->next = NULL; // Отсоединить от очереди
|
||||
entry->next = NULL;
|
||||
entry->prev = NULL; // Полностью отсоединить от очереди
|
||||
|
||||
// Уменьшить счетчик ссылок при извлечении из очереди
|
||||
// entry->ref_count был увеличен при добавлении в очередь
|
||||
// теперь уменьшаем, но не освобождаем, так как вызывающий код должен это сделать
|
||||
entry->ref_count--;
|
||||
|
||||
// Инвалидировать позиции
|
||||
q->version++;
|
||||
|
||||
// При извлечении элемента приостанавливаем коллбэки
|
||||
// Это предотвращает рекурсию если во время обработки добавляются новые элементы
|
||||
q->callback_suspended = 1;
|
||||
@@ -350,7 +428,11 @@ struct queue_waiter* queue_wait_threshold(struct ll_queue* q, int max_packets, s
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_wait_threshold: condition already met, count=%d<=%d, bytes=%zu<=%zu, calling callback",
|
||||
q->count, max_packets, q->total_bytes, max_bytes);
|
||||
callback(q, arg);
|
||||
free(waiter);
|
||||
if (q->pool) {
|
||||
memory_pool_free(q->pool, waiter);
|
||||
} else {
|
||||
free(waiter);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -398,3 +480,368 @@ void queue_get_pool_stats(struct ll_queue* q, size_t* waiter_allocations, size_t
|
||||
|
||||
memory_pool_get_stats(q->pool, waiter_allocations, waiter_reuse);
|
||||
}
|
||||
|
||||
// ==================== Управление позициями ====================
|
||||
|
||||
struct ll_queue_pos* queue_pos_new(struct ll_queue* queue) {
|
||||
if (!queue) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_new: queue is NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ll_queue_pos* pos = malloc(sizeof(struct ll_queue_pos));
|
||||
if (!pos) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_new: failed to allocate position");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pos->queue = queue;
|
||||
pos->current = NULL;
|
||||
pos->prev = NULL;
|
||||
pos->index = -1; // Перед head
|
||||
pos->valid = 1;
|
||||
pos->version = queue->version;
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_new: created position %p for queue %p", pos, queue);
|
||||
return pos;
|
||||
}
|
||||
|
||||
void queue_pos_free(struct ll_queue_pos* pos) {
|
||||
if (!pos) return;
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_free: freeing position %p", pos);
|
||||
free(pos);
|
||||
}
|
||||
|
||||
void queue_pos_reset(struct ll_queue_pos* pos) {
|
||||
if (!pos) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_reset: pos is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
// Обновить версию позиции при необходимости (вместо инвалидации)
|
||||
if (pos->queue->version != pos->version) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_reset: updating position version from %lu to %lu",
|
||||
(unsigned long)pos->version, (unsigned long)pos->queue->version);
|
||||
pos->version = pos->queue->version;
|
||||
}
|
||||
|
||||
pos->current = NULL;
|
||||
pos->prev = NULL;
|
||||
pos->index = -1;
|
||||
pos->valid = 1;
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_reset: position reset");
|
||||
}
|
||||
|
||||
int queue_pos_next(struct ll_queue_pos* pos) {
|
||||
if (!pos) return 0;
|
||||
|
||||
// Убедиться, что позиция валидна (автоматическое обновление при необходимости)
|
||||
if (!queue_pos_ensure_valid(pos)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!pos->current) {
|
||||
// Перед head - переходим к head
|
||||
if (!pos->queue->head) {
|
||||
// Очередь пуста - некуда переходить
|
||||
return 0;
|
||||
}
|
||||
pos->current = pos->queue->head;
|
||||
pos->prev = NULL;
|
||||
pos->index = 0;
|
||||
} else if (pos->current->next) {
|
||||
// Есть следующий элемент
|
||||
pos->prev = pos->current;
|
||||
pos->current = pos->current->next;
|
||||
pos->index++;
|
||||
} else {
|
||||
// Достигнут конец очереди
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_next: moved to index %d, entry %p",
|
||||
pos->index, pos->current);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int queue_pos_prev(struct ll_queue_pos* pos) {
|
||||
if (!pos) return 0;
|
||||
|
||||
// Убедиться, что позиция валидна (автоматическое обновление при необходимости)
|
||||
if (!queue_pos_ensure_valid(pos)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!pos->current) {
|
||||
// Уже перед head - дальше некуда
|
||||
return 0;
|
||||
} else if (pos->current->prev) {
|
||||
// Есть предыдущий элемент
|
||||
pos->current = pos->current->prev;
|
||||
pos->prev = pos->current->prev;
|
||||
pos->index--;
|
||||
} else {
|
||||
// Достигнуто начало очереди
|
||||
pos->current = NULL;
|
||||
pos->prev = NULL;
|
||||
pos->index = -1;
|
||||
}
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_prev: moved to index %d, entry %p",
|
||||
pos->index, pos->current);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int queue_pos_seek(struct ll_queue_pos* pos, int target_index) {
|
||||
if (!pos) return 0;
|
||||
|
||||
// Убедиться, что позиция валидна (автоматическое обновление при необходимости)
|
||||
if (!queue_pos_ensure_valid(pos)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (target_index < -1 || target_index >= pos->queue->count) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_seek: invalid target index %d (count=%d)",
|
||||
target_index, pos->queue->count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Простая реализация: начинаем с начала и идем вперед
|
||||
// Можно оптимизировать позже, если потребуется
|
||||
queue_pos_reset(pos);
|
||||
|
||||
for (int i = 0; i <= target_index; i++) {
|
||||
if (!queue_pos_next(pos)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_seek: failed to reach index %d", target_index);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_seek: seeked to index %d, entry %p",
|
||||
pos->index, pos->current);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int queue_pos_insert(struct ll_queue_pos* pos, struct ll_entry* entry, int mode) {
|
||||
if (!pos || !pos->valid || !entry) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_insert: invalid parameters");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pos->queue->version != pos->version) {
|
||||
pos->valid = 0;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_insert: position invalidated due to queue version change");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct ll_queue* q = pos->queue;
|
||||
|
||||
// Проверить лимит размера
|
||||
if (q->size_limit >= 0 && q->count >= q->size_limit) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_insert: size limit exceeded, freeing entry %p", entry);
|
||||
queue_entry_free(entry);
|
||||
return -1;
|
||||
}
|
||||
|
||||
entry->ref_count++;
|
||||
|
||||
if (mode == 0) {
|
||||
// Вставить перед current
|
||||
if (!pos->current) {
|
||||
// Вставить в head
|
||||
entry->next = q->head;
|
||||
entry->prev = NULL;
|
||||
if (q->head) {
|
||||
q->head->prev = entry;
|
||||
} else {
|
||||
q->tail = entry; // Очередь была пустой
|
||||
}
|
||||
q->head = entry;
|
||||
} else {
|
||||
// Вставить перед current
|
||||
entry->next = pos->current;
|
||||
entry->prev = pos->current->prev;
|
||||
|
||||
if (pos->current->prev) {
|
||||
pos->current->prev->next = entry;
|
||||
} else {
|
||||
q->head = entry; // Вставляем в начало
|
||||
}
|
||||
pos->current->prev = entry;
|
||||
}
|
||||
} else {
|
||||
// Вставить после current
|
||||
if (!pos->current) {
|
||||
// Вставить в head (current = NULL, значит перед head)
|
||||
entry->next = q->head;
|
||||
entry->prev = NULL;
|
||||
if (q->head) {
|
||||
q->head->prev = entry;
|
||||
} else {
|
||||
q->tail = entry; // Очередь была пустой
|
||||
}
|
||||
q->head = entry;
|
||||
} else {
|
||||
// Вставить после current
|
||||
entry->next = pos->current->next;
|
||||
entry->prev = pos->current;
|
||||
|
||||
if (pos->current->next) {
|
||||
pos->current->next->prev = entry;
|
||||
} else {
|
||||
q->tail = entry; // Вставляем в конец
|
||||
}
|
||||
pos->current->next = entry;
|
||||
}
|
||||
}
|
||||
|
||||
q->count++;
|
||||
q->total_bytes += entry->size;
|
||||
|
||||
// Обновить версию очереди и позиции (вместо полной инвалидации)
|
||||
q->version++;
|
||||
|
||||
// Обновить текущую позицию, если она активна
|
||||
if (pos->current) {
|
||||
pos->version = q->version;
|
||||
pos->valid = 1; // Сделать позицию снова валидной
|
||||
// Корректировка индекса в зависимости от режима вставки
|
||||
if (mode == 0) {
|
||||
// Вставка перед текущим - индекс остается тем же
|
||||
// pos->index не меняется
|
||||
} else {
|
||||
// Вставка после текущего - текущий элемент смещается
|
||||
pos->index++;
|
||||
}
|
||||
}
|
||||
// Позиция остается валидной - убрали строку pos->valid = 0;
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_insert: inserted entry %p, mode=%d, count=%d",
|
||||
entry, mode, q->count);
|
||||
|
||||
// Проверить ожидающие коллбэки
|
||||
check_waiters(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ll_entry* queue_pos_remove(struct ll_queue_pos* pos) {
|
||||
if (!pos || !pos->valid || !pos->current) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_remove: invalid position or no current element");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Убедиться, что позиция валидна (автоматическое обновление при необходимости)
|
||||
if (!queue_pos_ensure_valid(pos)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ll_queue* q = pos->queue;
|
||||
struct ll_entry* entry = pos->current;
|
||||
|
||||
// Удалить из двусвязного списка
|
||||
if (entry->prev) {
|
||||
entry->prev->next = entry->next;
|
||||
} else {
|
||||
q->head = entry->next; // Удаляем head
|
||||
}
|
||||
|
||||
if (entry->next) {
|
||||
entry->next->prev = entry->prev;
|
||||
} else {
|
||||
q->tail = entry->prev; // Удаляем tail
|
||||
}
|
||||
|
||||
q->count--;
|
||||
q->total_bytes -= entry->size;
|
||||
|
||||
// Обновить позицию: перейти к следующему элементу
|
||||
pos->current = entry->next;
|
||||
pos->prev = entry->prev;
|
||||
if (pos->current) {
|
||||
// Остались элементы после удаленного
|
||||
pos->index = pos->index; // Индекс остается тем же
|
||||
} else {
|
||||
// Удалили последний элемент
|
||||
pos->index = q->count; // Указывает за конец
|
||||
if (pos->index == 0) {
|
||||
pos->current = NULL;
|
||||
pos->prev = NULL;
|
||||
pos->index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
entry->next = NULL;
|
||||
entry->prev = NULL;
|
||||
entry->ref_count--; // Уменьшить счетчик ссылок
|
||||
|
||||
// Обновить версию очереди и позицию (вместо полной инвалидации)
|
||||
q->version++;
|
||||
if (pos->current) {
|
||||
pos->version = q->version;
|
||||
pos->valid = 1; // Сделать позицию снова валидной
|
||||
// pos->index уже обновлен выше
|
||||
}
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_pos_remove: removed entry %p, new count=%d",
|
||||
entry, q->count);
|
||||
|
||||
// Проверить ожидающие коллбэки
|
||||
check_waiters(q);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
int queue_entry_remove(struct ll_queue* q, struct ll_entry* entry) {
|
||||
if (!q || !entry) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "queue_entry_remove: invalid parameters");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Проверить, что элемент действительно в этой очереди
|
||||
struct ll_entry* curr = q->head;
|
||||
while (curr) {
|
||||
if (curr == entry) {
|
||||
break;
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
if (!curr) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "queue_entry_remove: entry %p not found in queue", entry);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Удалить из двусвязного списка
|
||||
if (entry->prev) {
|
||||
entry->prev->next = entry->next;
|
||||
} else {
|
||||
q->head = entry->next; // Удаляем head
|
||||
}
|
||||
|
||||
if (entry->next) {
|
||||
entry->next->prev = entry->prev;
|
||||
} else {
|
||||
q->tail = entry->prev; // Удаляем tail
|
||||
}
|
||||
|
||||
q->count--;
|
||||
q->total_bytes -= entry->size;
|
||||
|
||||
entry->next = NULL;
|
||||
entry->prev = NULL;
|
||||
entry->ref_count--;
|
||||
|
||||
// Инвалидировать позиции
|
||||
q->version++;
|
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_entry_remove: removed entry %p, new count=%d",
|
||||
entry, q->count);
|
||||
|
||||
// Проверить ожидающие коллбэки
|
||||
check_waiters(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user