make queue thread-safe
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "myqueue.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
#ifndef MYCLIB_QUEUE_H
|
||||
#define MYCLIB_QUEUE_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
||||
Reference in New Issue
Block a user