Skip to content

Commit

Permalink
new interface, tests, examples, readme, threadsafe
Browse files Browse the repository at this point in the history
- updated tests to test new interface
- corrected bugs found in tests
- changed what is and isn't classed as an error
- updated example
- updated readme
  • Loading branch information
Alex Young committed May 9, 2015
1 parent 396e6e7 commit 0b8cc2f
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 255 deletions.
45 changes: 33 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
# queue

A type-agnostic header-only macro-based struct queue implementation in C.
A thread safe type-agnostic header-only macro-based structure queue
implementation in C.

## Usage

To create a queueable struct, include the `queue_handle qh` member in your
struct.
Include the queue.h header file in your source. To create a queueable
structure, include the `queue_handle qh` member in the struct definition.

Before using QUEUE_PUSH, ensure that the queue struct is set to NULL, this is
the only initialisation that needs to be done.

This queue implementation is not thread safe.
Before any other operations, ensure that QUEUE_INIT has been called, it is an
error to not do so. This initialises the queue and sets up the mutex and
conditions variables. Setting the queue to NULL will protect against errors
if a queue has not been

Freeing the queue does not free every element in the queue if they have been
dynamically allocated. This has to be done by popping all the elements in the
queue and freeing them manually.

Pushing the same element (at the same address) into the queue is not supported.
This is because the `next` member of the queue's `queue_handle` will be
overwitten. This will case undefined behaviour when pushing or popping to or
from the queue (see commented out test case).
Pushing the same element (at the same address) into the queue is not supported
and is an error. This is because the `next` member of the queue's `queue_handle`
will be overwritten. This will cause undefined behavior when pushing or popping
to or from the queue (see commented out test case).

```c
#include <stdlib.h>
Expand All @@ -35,7 +36,7 @@ int main(void) {
struct msg *msgs; // message queue
struct msg m1, *m2;

msgs = NULL;
QUEUE_INIT(struct msg, msgs);

m1.content = "abc";
QUEUE_PUSH(msgs, &m1);
Expand All @@ -50,3 +51,23 @@ int main(void) {
return EXIT_SUCCESS;
}
```
## Testing and building examples
To run tests run
```bash
make check
```

To compile the examples run

```bash
make examples
```

Both of the above can be done together by running

```bash
make
```
3 changes: 1 addition & 2 deletions examples/simple.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ int main(void) {
struct msg *msgs; // message queue
struct msg m1, *m2;

msgs = malloc(sizeof (struct msg));
QUEUE_INIT(msgs);
QUEUE_INIT(struct msg, msgs);

m1.content = "abc";
QUEUE_PUSH(msgs, &m1);
Expand Down
84 changes: 45 additions & 39 deletions src/queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,63 +18,68 @@ typedef struct queue_handle {
void *next;
} queue_handle;

#define QUEUE_INIT(q) \
#define QUEUE_INIT(qt, q) \
do { \
(q)->qh.qc = malloc(sizeof (queue_core)); \
queue_core *qc = (q)->qh.qc; \
pthread_mutex_init(&qc->mutex, NULL); \
pthread_cond_init(&qc->cond, NULL); \
qc->front = qc->back = NULL; \
qc->backqh = NULL; \
qc->size = 0; \
(q)->qh.next = NULL; \
(q) = malloc(sizeof (qt)); \
if (q) { \
(q)->qh.qc = malloc(sizeof (queue_core)); \
if ((q)->qh.qc) { \
queue_core *qc = (q)->qh.qc; \
pthread_mutex_init(&qc->mutex, NULL); \
pthread_cond_init(&qc->cond, NULL); \
qc->front = qc->back = NULL; \
qc->backqh = NULL; \
qc->size = 0; \
(q)->qh.next = NULL; \
} else { \
free(q); \
(q) = NULL; \
} \
} \
} while (0)

#define QUEUE_PUSH(q, e) \
do { \
if (q && (q)->qh.qc) { \
queue_core *qc = (q)->qh.qc; \
queue_handle *backqh; \
pthread_mutex_lock(&qc->mutex); \
(e)->qh.qc = qc; \
(e)->qh.next = NULL; \
backqh = qc->backqh; \
if (!qc->front) { /* empty queue */ \
qc->front = qc->back = (e); \
} else { /* non-empty queue */ \
backqh->next = (e); \
qc->back = (e); \
} \
backqh = &(e)->qh; \
qc->size++; \
pthread_mutex_unlock(&qc->mutex); \
pthread_cond_signal(&qc->cond); /* broadcast to all? */ \
queue_core *qc = (q)->qh.qc; \
queue_handle *backqh; \
pthread_mutex_lock(&qc->mutex); \
(e)->qh.qc = qc; \
(e)->qh.next = NULL; \
backqh = qc->backqh; \
if (!qc->front) { /* empty queue */ \
qc->front = qc->back = (e); \
} else { /* non-empty queue */ \
backqh->next = (e); \
qc->back = (e); \
} \
backqh = &((e)->qh); \
qc->backqh = backqh; \
qc->size++; \
pthread_mutex_unlock(&qc->mutex); \
pthread_cond_signal(&qc->cond); /* broadcast to all? */ \
} while (0)

#define QUEUE_POP(q, e) \
do { \
(e) = NULL; \
if (q && (q)->qh.qc) { \
queue_core *qc = (q)->qh.qc; \
pthread_mutex_lock(&qc->mutex); \
while (QUEUE_SIZE(q) == 0) { \
pthread_cond_wait(&qc->cond, &qc->mutex); \
} \
if ((q)->qh.qc->front != NULL) { \
(e) = (q)->qh.qc->front; \
(q)->qh.qc->front = (e)->qh.next; \
(q)->qh.qc->size--; \
} \
pthread_mutex_unlock(&qc->mutex); \
queue_core *qc = (q)->qh.qc; \
pthread_mutex_lock(&qc->mutex); \
while (QUEUE_SIZE(q) == 0) { \
pthread_cond_wait(&qc->cond, &qc->mutex); \
} \
if ((q)->qh.qc->front != NULL) { \
(e) = (q)->qh.qc->front; \
(q)->qh.qc->front = (e)->qh.next; \
(q)->qh.qc->size--; \
} \
pthread_mutex_unlock(&qc->mutex); \
} while (0)

#define QUEUE_LOCK(q) \
pthread_mutex_lock(&q->qh.qc->mutex);

#define QUEUE_SIZE(q) \
((q) ? (q)->qh.qc->size : 0U)
((q)->qh.qc->size)

#define QUEUE_UNLOCK(q) \
pthread_mutex_unlock(&q->qh.qc->mutex);
Expand All @@ -86,6 +91,7 @@ typedef struct queue_handle {
pthread_cond_destroy(&qc->cond); \
pthread_mutex_destroy(&qc->mutex); \
free(qc); \
free(q); \
} \
} while (0)

Expand Down
Loading

0 comments on commit 0b8cc2f

Please sign in to comment.