Commit 42b7ab48 authored by David Gibson's avatar David Gibson

lqueue: Streamline interface with TCON_CONTAINER

The interfaces the lqueue module currently has implement (partial) type
safety in a somewhat clunky way - types and member names need to be passed
to a number of entry points.

This patch uses the new TCON_CONTAINER magic to stash the typing
information into the declaration of the queue object, so it no longer needs
to be explicitly passed to later calls.

This does alter the lqueue interface incompatibly.  I think the module
is young enough that we can reasonably do that.  One other module,
'aga', was using lqueue, and this also includes fixes so that it still
works.
Signed-off-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
parent e47d78e9
...@@ -12,25 +12,27 @@ ...@@ -12,25 +12,27 @@
* Breadth first search * Breadth first search
*/ */
static bool bfs_enqueue(struct aga_graph *g, struct lqueue *queue, typedef LQUEUE(struct aga_node, u.bfs.next) bfs_queue;
static bool bfs_enqueue(struct aga_graph *g, bfs_queue *queue,
struct aga_node *n) struct aga_node *n)
{ {
if (!aga_update_node(g, n)) if (!aga_update_node(g, n))
return false; return false;
lqueue_enqueue(queue, n, u.bfs.next); lqueue_enqueue(queue, n);
n->u.bfs.edge = aga_first_edge(g, n); n->u.bfs.edge = aga_first_edge(g, n);
return true; return true;
} }
static struct aga_node *bfs_front(struct lqueue *queue) static struct aga_node *bfs_front(bfs_queue *queue)
{ {
return lqueue_front(queue, struct aga_node, u.bfs.next); return lqueue_front(queue);
} }
static void bfs_dequeue(struct lqueue *queue) static void bfs_dequeue(bfs_queue *queue)
{ {
lqueue_dequeue(queue, struct aga_node, u.bfs.next); lqueue_dequeue(queue);
} }
int aga_bfs_start(struct aga_graph *g) int aga_bfs_start(struct aga_graph *g)
...@@ -46,7 +48,7 @@ int aga_bfs_start(struct aga_graph *g) ...@@ -46,7 +48,7 @@ int aga_bfs_start(struct aga_graph *g)
struct aga_node *aga_bfs_explore(struct aga_graph *g, struct aga_node *n) struct aga_node *aga_bfs_explore(struct aga_graph *g, struct aga_node *n)
{ {
LQUEUE(queue); bfs_queue queue = LQUEUE_INIT;
if (!aga_check_state(g)) if (!aga_check_state(g))
return NULL; return NULL;
...@@ -57,7 +59,7 @@ struct aga_node *aga_bfs_explore(struct aga_graph *g, struct aga_node *n) ...@@ -57,7 +59,7 @@ struct aga_node *aga_bfs_explore(struct aga_graph *g, struct aga_node *n)
if (bfs_enqueue(g, &queue, n)) if (bfs_enqueue(g, &queue, n))
return n; return n;
lqueue_init_from_back(&queue, n, u.bfs.next); lqueue_init_from_back(&queue, n);
while ((n = bfs_front(&queue))) { while ((n = bfs_front(&queue))) {
const void *e = n->u.bfs.edge; const void *e = n->u.bfs.edge;
......
...@@ -23,18 +23,18 @@ ...@@ -23,18 +23,18 @@
* { * {
* int i; * int i;
* struct arg *a; * struct arg *a;
* LQUEUE(argq); * LQUEUE(struct arg, ql) argq = LQUEUE_INIT;
* *
* for (i = 0; i < argc; i++) { * for (i = 0; i < argc; i++) {
* a = malloc(sizeof(*a)); * a = malloc(sizeof(*a));
* a->arg = argv[i]; * a->arg = argv[i];
* lqueue_enqueue(&argq, a, ql); * lqueue_enqueue(&argq, a);
* } * }
* *
* printf("Command line arguments in order:\n"); * printf("Command line arguments in order:\n");
* *
* while (!lqueue_empty(&argq)) { * while (!lqueue_empty(&argq)) {
* a = lqueue_dequeue(&argq, struct arg, ql); * a = lqueue_dequeue(&argq);
* printf("Argument: %s\n", a->arg); * printf("Argument: %s\n", a->arg);
* free(a); * free(a);
* } * }
...@@ -49,7 +49,7 @@ int main(int argc, char *argv[]) ...@@ -49,7 +49,7 @@ int main(int argc, char *argv[])
return 1; return 1;
if (strcmp(argv[1], "depends") == 0) { if (strcmp(argv[1], "depends") == 0) {
printf("ccan/container_of\n"); printf("ccan/tcon\n");
return 0; return 0;
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <ccan/container_of/container_of.h> #include <ccan/tcon/tcon.h>
/** /**
* struct lqueue_link - a queue link * struct lqueue_link - a queue link
...@@ -25,36 +25,74 @@ struct lqueue_link { ...@@ -25,36 +25,74 @@ struct lqueue_link {
}; };
/** /**
* struct lqueue - the head of a queue * struct lqueue_ - a queue (internal type)
* @b: the back of the queue (NULL if empty) * @b: the back of the queue (NULL if empty)
*/ */
struct lqueue { struct lqueue_ {
struct lqueue_link *back; struct lqueue_link *back;
}; };
/** /**
* LQUEUE - define and initialize an empty queue * LQUEUE - declare a queue
* @name: the name of the lqueue. * @type: the type of elements in the queue
* @link: the field containing the lqueue_link in @type
* *
* The LQUEUE macro defines an lqueue and initializes it to an empty * The LQUEUE macro declares an lqueue. It can be prepended by
* queue. It can be prepended by "static" to define a static lqueue. * "static" to define a static lqueue. The queue begins in undefined
* state, you must either initialize with LQUEUE_INIT, or call
* lqueue_init() before using it.
* *
* See also: * See also:
* lqueue_init() * lqueue_init()
* *
* Example: * Example:
* LQUEUE(my_queue); * struct element {
* int value;
* struct lqueue_link link;
* };
* LQUEUE(struct element, link) my_queue;
*/
#define LQUEUE(etype, link) \
TCON_WRAP(struct lqueue_, \
TCON_CONTAINER(canary, etype, link))
/**
* LQUEUE_INIT - initializer for an empty queue
*
* The LQUEUE_INIT macro returns a suitable initializer for a queue
* defined with LQUEUE.
*
* Example:
* struct element {
* int value;
* struct lqueue_link link;
* };
* LQUEUE(struct element, link) my_queue = LQUEUE_INIT;
* *
* assert(lqueue_empty(&my_queue)); * assert(lqueue_empty(&my_queue));
*/ */
#define LQUEUE(name) \ #define LQUEUE_INIT \
struct lqueue name = { NULL, } TCON_WRAP_INIT({ NULL, })
/**
* lqueue_entry - convert an lqueue_link back into the structure containing it.
* @q: the queue
* @l: the lqueue_link
*
* Example:
* struct waiter {
* char *name;
* struct lqueue_link ql;
* } w;
* LQUEUE(struct waiter, ql) my_queue;
* assert(lqueue_entry(&my_queue, &w.ql) == &w);
*/
#define lqueue_entry(q_, l_) tcon_container_of((q_), canary, (l_))
/** /**
* lqueue_init_from_back - initialize a queue with a specific back element * lqueue_init_from_back - initialize a queue with a specific back element
* @s: the lqueue to initialize * @s: the lqueue to initialize
* @e: pointer to the back element of the new queue * @e: pointer to the back element of the new queue
* @member: member of the element containing the lqueue_link
* *
* USE WITH CAUTION: This is for handling unusual cases where you have * USE WITH CAUTION: This is for handling unusual cases where you have
* a pointer to an element in a previously constructed queue but can't * a pointer to an element in a previously constructed queue but can't
...@@ -62,32 +100,35 @@ struct lqueue { ...@@ -62,32 +100,35 @@ struct lqueue {
* should use lqueue_init(). * should use lqueue_init().
* *
* Example: * Example:
* LQUEUE(queue1);
* struct lqueue queue2;
* struct element { * struct element {
* int value; * int value;
* struct lqueue_link link; * struct lqueue_link link;
* } el; * } el;
* LQUEUE(struct element, link) queue1;
* LQUEUE(struct element, link) queue2;
* *
* lqueue_enqueue(&queue1, &el, link); * lqueue_enqueue(&queue1, &el);
* *
* lqueue_init_from_back(&queue2, * lqueue_init_from_back(&queue2, lqueue_back(&queue1));
* lqueue_back(&queue1, struct element, link), link);
*/ */
#define lqueue_init_from_back(s, e, member) \ #define lqueue_init_from_back(q_, e_) \
(lqueue_init_((s), &(e)->member)) (lqueue_init_(tcon_unwrap(q_), tcon_member_of((q_), canary, (e_))))
/** /**
* lqueue_init - initialize a queue * lqueue_init - initialize a queue
* @h: the lqueue to set to an empty queue * @h: the lqueue to set to an empty queue
* *
* Example: * Example:
* struct lqueue *qp = malloc(sizeof(*qp)); * struct element {
* int value;
* struct lqueue_link link;
* };
* LQUEUE(struct element, link) *qp = malloc(sizeof(*qp));
* lqueue_init(qp); * lqueue_init(qp);
*/ */
#define lqueue_init(s) \ #define lqueue_init(q_) \
(lqueue_init_((s), NULL)) (lqueue_init_(tcon_unwrap(q_), NULL))
static inline void lqueue_init_(struct lqueue *q, struct lqueue_link *back) static inline void lqueue_init_(struct lqueue_ *q, struct lqueue_link *back)
{ {
q->back = back; q->back = back;
} }
...@@ -97,47 +138,29 @@ static inline void lqueue_init_(struct lqueue *q, struct lqueue_link *back) ...@@ -97,47 +138,29 @@ static inline void lqueue_init_(struct lqueue *q, struct lqueue_link *back)
* @q: the queue * @q: the queue
* *
* If the queue is empty, returns true. * If the queue is empty, returns true.
*
* Example:
* assert(lqueue_empty(qp));
*/ */
static inline bool lqueue_empty(const struct lqueue *q) #define lqueue_empty(q_) \
lqueue_empty_(tcon_unwrap(q_))
static inline bool lqueue_empty_(const struct lqueue_ *q)
{ {
return (q->back == NULL); return (q->back == NULL);
} }
/**
* lqueue_entry - convert an lqueue_link back into the structure containing it.
* @e: the lqueue_link
* @type: the type of the entry
* @member: the lqueue_link member of the type
*
* Example:
* struct waiter {
* char *name;
* struct lqueue_link ql;
* } w;
* assert(lqueue_entry(&w.ql, struct waiter, ql) == &w);
*/
#define lqueue_entry(n, type, member) container_of_or_null(n, type, member)
/** /**
* lqueue_front - get front entry in a queue * lqueue_front - get front entry in a queue
* @q: the queue * @q: the queue
* @type: the type of queue entries
* @member: the lqueue_link entry
* *
* If the queue is empty, returns NULL. * If the queue is empty, returns NULL.
* *
* Example: * Example:
* struct waiter *f; * struct element *f;
* *
* f = lqueue_front(qp, struct waiter, ql); * f = lqueue_front(qp);
* assert(lqueue_dequeue(qp, struct waiter, ql) == f); * assert(lqueue_dequeue(qp) == f);
*/ */
#define lqueue_front(q, type, member) \ #define lqueue_front(q_) \
lqueue_entry(lqueue_front_((q)), type, member) lqueue_entry((q_), lqueue_front_(tcon_unwrap(q_)))
static inline struct lqueue_link *lqueue_front_(const struct lqueue *q) static inline struct lqueue_link *lqueue_front_(const struct lqueue_ *q)
{ {
if (!q->back) if (!q->back)
return NULL; return NULL;
...@@ -148,20 +171,18 @@ static inline struct lqueue_link *lqueue_front_(const struct lqueue *q) ...@@ -148,20 +171,18 @@ static inline struct lqueue_link *lqueue_front_(const struct lqueue *q)
/** /**
* lqueue_back - get back entry in a queue * lqueue_back - get back entry in a queue
* @q: the queue * @q: the queue
* @type: the type of queue entries
* @member: the lqueue_link entry
* *
* If the queue is empty, returns NULL. * If the queue is empty, returns NULL.
* *
* Example: * Example:
* struct waiter b; * struct element b;
* *
* lqueue_enqueue(qp, &b, ql); * lqueue_enqueue(qp, &b);
* assert(lqueue_back(qp, struct waiter, ql) == &b); * assert(lqueue_back(qp) == &b);
*/ */
#define lqueue_back(q, type, member) \ #define lqueue_back(q_) \
lqueue_entry(lqueue_back_((q)), type, member) lqueue_entry((q_), lqueue_back_(tcon_unwrap(q_)))
static inline struct lqueue_link *lqueue_back_(const struct lqueue *q) static inline struct lqueue_link *lqueue_back_(const struct lqueue_ *q)
{ {
return q->back; return q->back;
} }
...@@ -170,15 +191,14 @@ static inline struct lqueue_link *lqueue_back_(const struct lqueue *q) ...@@ -170,15 +191,14 @@ static inline struct lqueue_link *lqueue_back_(const struct lqueue *q)
* lqueue_enqueue - add an entry to the back of a queue * lqueue_enqueue - add an entry to the back of a queue
* @q: the queue to add the node to * @q: the queue to add the node to
* @e: the item to enqueue * @e: the item to enqueue
* @member: the lqueue_link field of *e
* *
* The lqueue_link does not need to be initialized; it will be overwritten. * The lqueue_link does not need to be initialized; it will be overwritten.
*/ */
#define lqueue_enqueue(q, e, member) \ #define lqueue_enqueue(q_, e_) \
lqueue_enqueue_((q), &((e)->member)) lqueue_enqueue_(tcon_unwrap(q_), tcon_member_of((q_), canary, (e_)))
static inline void lqueue_enqueue_(struct lqueue *q, struct lqueue_link *e) static inline void lqueue_enqueue_(struct lqueue_ *q, struct lqueue_link *e)
{ {
if (lqueue_empty(q)) { if (lqueue_empty_(q)) {
/* New entry will be both front and back of queue */ /* New entry will be both front and back of queue */
e->next = e; e->next = e;
q->back = e; q->back = e;
...@@ -192,19 +212,17 @@ static inline void lqueue_enqueue_(struct lqueue *q, struct lqueue_link *e) ...@@ -192,19 +212,17 @@ static inline void lqueue_enqueue_(struct lqueue *q, struct lqueue_link *e)
/** /**
* lqueue_dequeue - remove and return the entry from the front of the queue * lqueue_dequeue - remove and return the entry from the front of the queue
* @q: the queue * @q: the queue
* @type: the type of queue entries
* @member: the lqueue_link field of @type
* *
* Note that this leaves the returned entry's link in an undefined * Note that this leaves the returned entry's link in an undefined
* state; it can be added to another queue, but not deleted again. * state; it can be added to another queue, but not deleted again.
*/ */
#define lqueue_dequeue(q, type, member) \ #define lqueue_dequeue(q_) \
lqueue_entry(lqueue_dequeue_((q)), type, member) lqueue_entry((q_), lqueue_dequeue_(tcon_unwrap(q_)))
static inline struct lqueue_link *lqueue_dequeue_(struct lqueue *q) static inline struct lqueue_link *lqueue_dequeue_(struct lqueue_ *q)
{ {
struct lqueue_link *front; struct lqueue_link *front;
if (lqueue_empty(q)) if (lqueue_empty_(q))
return NULL; return NULL;
front = lqueue_front_(q); front = lqueue_front_(q);
......
...@@ -10,7 +10,7 @@ struct waiter { ...@@ -10,7 +10,7 @@ struct waiter {
int main(void) int main(void)
{ {
LQUEUE(q); LQUEUE(struct waiter, ql) q = LQUEUE_INIT;
struct waiter a = { "Alice" }; struct waiter a = { "Alice" };
struct waiter b = { "Bob" }; struct waiter b = { "Bob" };
struct waiter c = { "Carol" }; struct waiter c = { "Carol" };
...@@ -20,49 +20,49 @@ int main(void) ...@@ -20,49 +20,49 @@ int main(void)
plan_tests(25); plan_tests(25);
ok1(lqueue_empty(&q)); ok1(lqueue_empty(&q));
ok1(lqueue_front(&q, struct waiter, ql) == NULL); ok1(lqueue_front(&q) == NULL);
ok1(lqueue_back(&q, struct waiter, ql) == NULL); ok1(lqueue_back(&q) == NULL);
lqueue_enqueue(&q, &a, ql); lqueue_enqueue(&q, &a);
ok1(!lqueue_empty(&q)); ok1(!lqueue_empty(&q));
ok1(lqueue_front(&q, struct waiter, ql) == &a); ok1(lqueue_front(&q) == &a);
ok1(lqueue_back(&q, struct waiter, ql) == &a); ok1(lqueue_back(&q) == &a);
lqueue_enqueue(&q, &b, ql); lqueue_enqueue(&q, &b);
ok1(!lqueue_empty(&q)); ok1(!lqueue_empty(&q));
ok1(lqueue_front(&q, struct waiter, ql) == &a); ok1(lqueue_front(&q) == &a);
ok1(lqueue_back(&q, struct waiter, ql) == &b); ok1(lqueue_back(&q) == &b);
lqueue_enqueue(&q, &c, ql); lqueue_enqueue(&q, &c);
ok1(!lqueue_empty(&q)); ok1(!lqueue_empty(&q));
ok1(lqueue_front(&q, struct waiter, ql) == &a); ok1(lqueue_front(&q) == &a);
ok1(lqueue_back(&q, struct waiter, ql) == &c); ok1(lqueue_back(&q) == &c);
waiter = lqueue_dequeue(&q, struct waiter, ql); waiter = lqueue_dequeue(&q);
ok1(waiter == &a); ok1(waiter == &a);
ok1(!lqueue_empty(&q)); ok1(!lqueue_empty(&q));
ok1(lqueue_front(&q, struct waiter, ql) == &b); ok1(lqueue_front(&q) == &b);
ok1(lqueue_back(&q, struct waiter, ql) == &c); ok1(lqueue_back(&q) == &c);
waiter = lqueue_dequeue(&q, struct waiter, ql); waiter = lqueue_dequeue(&q);
ok1(waiter == &b); ok1(waiter == &b);
ok1(!lqueue_empty(&q)); ok1(!lqueue_empty(&q));
ok1(lqueue_front(&q, struct waiter, ql) == &c); ok1(lqueue_front(&q) == &c);
ok1(lqueue_back(&q, struct waiter, ql) == &c); ok1(lqueue_back(&q) == &c);
waiter = lqueue_dequeue(&q, struct waiter, ql); waiter = lqueue_dequeue(&q);
ok1(waiter == &c); ok1(waiter == &c);
ok1(lqueue_empty(&q)); ok1(lqueue_empty(&q));
ok1(lqueue_front(&q, struct waiter, ql) == NULL); ok1(lqueue_front(&q) == NULL);
ok1(lqueue_back(&q, struct waiter, ql) == NULL); ok1(lqueue_back(&q) == NULL);
ok1(lqueue_dequeue(&q, struct waiter, ql) == NULL); ok1(lqueue_dequeue(&q) == NULL);
/* This exits depending on whether all tests passed */ /* This exits depending on whether all tests passed */
return exit_status(); return exit_status();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment