[winpr,queue] improve queue management

* Fix Queue_EnsureCapacity reallocation handling, ensure queue->tail is
  properly updated.
* Fix allocation behaviour, use growthFactor * 32 as block size.
* Add doxygen for Queue_New
This commit is contained in:
Armin Novak
2026-02-18 09:27:56 +01:00
parent c0d477d278
commit b78cb455cb
2 changed files with 72 additions and 36 deletions

View File

@@ -181,6 +181,14 @@ extern "C"
WINPR_API void Queue_Free(wQueue* queue);
/** @brief Creates a new queue
*
* @param synchronized If \b TRUE all functions are thread safe, if \b FALSE no synchronization
* is done.
* @param capacity The initial capacity of the queue. If \b 0 or \b -1 default settings are
* applied.
* @param growthFactor allocation behaviour when the queue capacity should be increased. Larger
* values increase the allocation contingent. \b 0 or \b -1 apply default settings.
*
*
* @return A newly allocated queue or \b NULL in case of failure
*/

View File

@@ -35,7 +35,7 @@ struct s_wQueue
size_t head;
size_t tail;
size_t size;
void** array;
uintptr_t* array;
CRITICAL_SECTION lock;
HANDLE event;
@@ -45,6 +45,16 @@ struct s_wQueue
BYTE padding2[4];
};
static inline void* uptr2void(uintptr_t ptr)
{
return (void*)ptr;
}
static inline uintptr_t void2uptr(const void* ptr)
{
return (uintptr_t)ptr;
}
/**
* C equivalent of the C# Queue Class:
* http://msdn.microsoft.com/en-us/library/system.collections.queue.aspx
@@ -124,9 +134,12 @@ void Queue_Clear(wQueue* queue)
for (size_t index = queue->head; index != queue->tail; index = (index + 1) % queue->capacity)
{
if (queue->object.fnObjectFree)
queue->object.fnObjectFree(queue->array[index]);
{
void* obj = uptr2void(queue->array[index]);
queue->object.fnObjectFree(obj);
}
queue->array[index] = NULL;
queue->array[index] = 0;
}
queue->size = 0;
@@ -147,7 +160,8 @@ BOOL Queue_Contains(wQueue* queue, const void* obj)
for (size_t index = 0; index < queue->tail; index++)
{
if (queue->object.fnObjectEquals(queue->array[index], obj))
void* ptr = uptr2void(queue->array[index]);
if (queue->object.fnObjectEquals(ptr, obj))
{
found = TRUE;
break;
@@ -163,29 +177,52 @@ static BOOL Queue_EnsureCapacity(wQueue* queue, size_t count)
{
WINPR_ASSERT(queue);
if (queue->size + count >= queue->capacity)
if (queue->size + count > queue->capacity)
{
if (queue->growthFactor > SIZE_MAX / 32ull)
return FALSE;
if (queue->size > SIZE_MAX - count)
return FALSE;
const size_t increment = 32ull * queue->growthFactor;
const size_t old_capacity = queue->capacity;
size_t new_capacity = queue->capacity * queue->growthFactor;
void** newArray = NULL;
if (new_capacity < queue->size + count)
new_capacity = queue->size + count;
newArray = (void**)realloc((void*)queue->array, sizeof(void*) * new_capacity);
const size_t required = queue->size + count;
const size_t new_capacity = required + increment - required % increment;
if (new_capacity > SIZE_MAX / sizeof(BYTE*))
return FALSE;
uintptr_t* newArray = (uintptr_t*)realloc(queue->array, sizeof(uintptr_t) * new_capacity);
if (!newArray)
return FALSE;
queue->capacity = new_capacity;
queue->array = newArray;
ZeroMemory((void*)&(queue->array[old_capacity]),
(new_capacity - old_capacity) * sizeof(void*));
ZeroMemory(&(queue->array[old_capacity]),
(new_capacity - old_capacity) * sizeof(uintptr_t));
/* rearrange wrapped entries */
if (queue->tail <= queue->head)
{
CopyMemory((void*)&(queue->array[old_capacity]), (void*)queue->array,
queue->tail * sizeof(void*));
queue->tail += old_capacity;
const size_t tocopy = queue->tail;
const size_t slots = new_capacity - old_capacity;
const size_t batch = (tocopy < slots) ? tocopy : slots;
CopyMemory(&(queue->array[old_capacity]), queue->array, batch * sizeof(uintptr_t));
ZeroMemory(queue->array, batch * sizeof(uintptr_t));
/* Tail is decremented. if the whole thing is appended
* just move the existing tail by old_capacity */
if (tocopy < slots)
queue->tail += old_capacity;
else
{
const size_t movesize = (queue->tail - batch) * sizeof(uintptr_t);
memmove_s(queue->array, queue->tail * sizeof(uintptr_t), &queue->array[batch],
movesize);
ZeroMemory(&queue->array[batch], movesize);
queue->tail -= batch;
}
}
}
return TRUE;
@@ -205,17 +242,10 @@ BOOL Queue_Enqueue(wQueue* queue, const void* obj)
goto out;
if (queue->object.fnObjectNew)
queue->array[queue->tail] = queue->object.fnObjectNew(obj);
queue->array[queue->tail] = void2uptr(queue->object.fnObjectNew(obj));
else
{
union
{
const void* cv;
void* v;
} cnv;
cnv.cv = obj;
queue->array[queue->tail] = cnv.v;
}
queue->array[queue->tail] = void2uptr(obj);
queue->tail = (queue->tail + 1) % queue->capacity;
{
@@ -244,8 +274,8 @@ void* Queue_Dequeue(wQueue* queue)
if (queue->size > 0)
{
obj = queue->array[queue->head];
queue->array[queue->head] = NULL;
obj = uptr2void(queue->array[queue->head]);
queue->array[queue->head] = 0;
queue->head = (queue->head + 1) % queue->capacity;
queue->size--;
}
@@ -265,11 +295,10 @@ void* Queue_Dequeue(wQueue* queue)
void* Queue_Peek(wQueue* queue)
{
void* obj = NULL;
Queue_Lock(queue);
if (queue->size > 0)
obj = queue->array[queue->head];
obj = uptr2void(queue->array[queue->head]);
Queue_Unlock(queue);
@@ -299,9 +328,7 @@ static BOOL default_queue_equals(const void* obj1, const void* obj2)
wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor)
{
wObject* obj = NULL;
wQueue* queue = NULL;
queue = (wQueue*)calloc(1, sizeof(wQueue));
wQueue* queue = (wQueue*)calloc(1, sizeof(wQueue));
if (!queue)
return NULL;
@@ -325,9 +352,10 @@ wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor)
if (!queue->event)
goto fail;
obj = Queue_Object(queue);
obj->fnObjectEquals = default_queue_equals;
{
wObject* obj = Queue_Object(queue);
obj->fnObjectEquals = default_queue_equals;
}
return queue;
fail:
WINPR_PRAGMA_DIAG_PUSH
@@ -348,6 +376,6 @@ void Queue_Free(wQueue* queue)
DeleteCriticalSection(&queue->lock);
}
(void)CloseHandle(queue->event);
free((void*)queue->array);
free(queue->array);
free(queue);
}