diff --git a/README.md b/README.md index 08fc1a9..827dd90 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,4 @@ It is not production-ready. Use at your own risk. - Hashmaps - Thread-safe (MT Safe) - Dynamic strings - Thread-safe (MT Safe) > Requires linking with the C math library (`-lm`) -- Circular Queue - Not thread-safe (MT Unsafe) +- Circular Queue - Thread-safe (MT Safe) diff --git a/queue/myqueue.c b/queue/myqueue.c index 49fe73e..85a4666 100644 --- a/queue/myqueue.c +++ b/queue/myqueue.c @@ -1,5 +1,6 @@ #include "myqueue.h" +#include #include #include @@ -16,6 +17,14 @@ mcl_queue *mcl_queue_init(size_t queue_size, size_t elem_size) { return NULL; } + int ret = pthread_mutex_init(&queue->lock, NULL); + if (ret != 0) { + free(queue->buffer); + free(queue); + + return NULL; + } + queue->front = 0; queue->rear = 0; queue->size = 0; @@ -26,8 +35,15 @@ mcl_queue *mcl_queue_init(size_t queue_size, size_t elem_size) { } int mcl_queue_push(mcl_queue *queue, const void *elem) { + int ret = pthread_mutex_lock(&queue->lock); + if (ret != 0) { + return -1; + } + if (queue->size == queue->capacity) { /* Queue full */ + pthread_mutex_unlock(&queue->lock); + return -1; } @@ -38,12 +54,21 @@ int mcl_queue_push(mcl_queue *queue, const void *elem) { queue->size++; queue->rear = (queue->rear + 1) % queue->capacity; + pthread_mutex_unlock(&queue->lock); + return 0; } int mcl_queue_pop(mcl_queue *queue, void *out_elem) { + int ret = pthread_mutex_lock(&queue->lock); + if (ret != 0) { + return -1; + } + if (queue->size == 0) { /* Queue empty */ + pthread_mutex_unlock(&queue->lock); + return -1; } @@ -53,20 +78,41 @@ int mcl_queue_pop(mcl_queue *queue, void *out_elem) { queue->front = (queue->front + 1) % queue->capacity; queue->size--; + pthread_mutex_unlock(&queue->lock); + return 0; } -void *mcl_queue_get_front(mcl_queue *queue) { - if (queue->size == 0) { - return NULL; +int mcl_queue_get_front(mcl_queue *queue, void *out) { + int ret = pthread_mutex_lock(&queue->lock); + if (ret != 0) { + return -1; } - return (void *)queue->buffer + (queue->front * queue->elem_size); + if (queue->size == 0) { + pthread_mutex_unlock(&queue->lock); + + return -1; + } + + void *front = (void *)queue->buffer + (queue->front * queue->elem_size); + memcpy(out, front, queue->elem_size); + + pthread_mutex_unlock(&queue->lock); + + return 0; } -void *mcl_queue_get_rear(mcl_queue *queue) { +int mcl_queue_get_rear(mcl_queue *queue, void *out) { + int ret = pthread_mutex_lock(&queue->lock); + if (ret != 0) { + return -1; + } + if (queue->size == 0) { - return NULL; + pthread_mutex_unlock(&queue->lock); + + return -1; } size_t rear_index; @@ -76,7 +122,12 @@ void *mcl_queue_get_rear(mcl_queue *queue) { rear_index = queue->rear - 1; } - return (void *)queue->buffer + (rear_index * queue->elem_size); + void *rear = (void *)queue->buffer + (rear_index * queue->elem_size); + memcpy(out, rear, queue->elem_size); + + pthread_mutex_unlock(&queue->lock); + + return 0; } void mcl_queue_free(mcl_queue *queue) { @@ -84,6 +135,8 @@ void mcl_queue_free(mcl_queue *queue) { return; } + pthread_mutex_destroy(&queue->lock); + free(queue->buffer); free(queue); } diff --git a/queue/myqueue.h b/queue/myqueue.h index 42d4bfd..4ad11d0 100644 --- a/queue/myqueue.h +++ b/queue/myqueue.h @@ -1,26 +1,28 @@ #ifndef MYCLIB_QUEUE_H #define MYCLIB_QUEUE_H +#include #include /** - * @brief A generic circular queue (ring buffer). + * @brief A simple circular queue (ring buffer). */ typedef struct mcl_queue_t { - size_t front; /**< Index of the first element (read position). */ - size_t rear; /**< Index where the next element will be written (write position). */ - size_t size; /**< Current number of elements in the queue. */ - size_t capacity; /**< Maximum number of elements the queue can hold. */ - size_t elem_size; /**< Size in bytes of each element in the queue. */ - void *buffer; /**< Pointer to the memory buffer that stores the elements. */ + size_t front; /**< Index of the next element to read. */ + size_t rear; /**< Index where the next element will be written. */ + size_t size; /**< Current number of elements in the queue. */ + size_t capacity; /**< Maximum number of elements the queue can hold. */ + size_t elem_size; /**< Size in bytes of each element. */ + void *buffer; /**< Memory buffer that holds the elements. */ + pthread_mutex_t lock; /**< Mutex to protect concurrent access. */ } mcl_queue; /** * @brief Create and initialize a new queue. * * @param queue_size Number of elements the queue can hold. - * @param elem_size Size (in bytes) of each element in the queue. - * @return Pointer to the new queue, or NULL if allocation fails. + * @param elem_size Size in bytes of each element. + * @return Pointer to the new queue, or NULL on failure. */ mcl_queue *mcl_queue_init(size_t queue_size, size_t elem_size); @@ -28,41 +30,43 @@ mcl_queue *mcl_queue_init(size_t queue_size, size_t elem_size); * @brief Add an element to the queue. * * @param queue Pointer to the queue. - * @param elem Pointer to the element to insert. - * @return 0 on success, -1 if the queue is full. + * @param elem Pointer to the data to add. + * @return 0 on success, -1 if the queue is full or on error. */ int mcl_queue_push(mcl_queue *queue, const void *elem); /** * @brief Remove an element from the queue. * - * @param queue Pointer to the queue. - * @param out_elem Pointer where the removed element will be copied. - * @return 0 on success, -1 if the queue is empty. + * @param queue Pointer to the queue. + * @param out_elem Pointer to memory where the removed element will be copied. + * @return 0 on success, -1 if the queue is empty or on error. */ int mcl_queue_pop(mcl_queue *queue, void *out_elem); /** - * @brief Get a pointer to the front element of the queue (oldest one). + * @brief Copy the front element without removing it. * * @param queue Pointer to the queue. - * @return Pointer to the front element, or NULL if the queue is empty. + * @param out Pointer to memory where the element will be copied. + * @return 0 on success, -1 if the queue is empty or on error. */ -void *mcl_queue_get_front(mcl_queue *queue); +int mcl_queue_get_front(mcl_queue *queue, void *out); /** - * @brief Get a pointer to the rear element of the queue (most recently added). + * @brief Copy the last element without removing it. * * @param queue Pointer to the queue. - * @return Pointer to the rear element, or NULL if the queue is empty. + * @param out Pointer to memory where the element will be copied. + * @return 0 on success, -1 if the queue is empty or on error. */ -void *mcl_queue_get_rear(mcl_queue *queue); +int mcl_queue_get_rear(mcl_queue *queue, void *out); /** - * @brief Free all memory used by the queue. + * @brief Free all resources used by the queue. * * @param queue Pointer to the queue to free. */ void mcl_queue_free(mcl_queue *queue); -#endif +#endif // MYCLIB_QUEUE_H