1 void	mq_nanosleep(unsigned ns);
2 
3 /*
4  * Simple templated message queue implementation that relies on only mutexes for
5  * synchronization (which reduces portability issues).  Given the following
6  * setup:
7  *
8  *   typedef struct mq_msg_s mq_msg_t;
9  *   struct mq_msg_s {
10  *           mq_msg(mq_msg_t) link;
11  *           [message data]
12  *   };
13  *   mq_gen(, mq_, mq_t, mq_msg_t, link)
14  *
15  * The API is as follows:
16  *
17  *   bool mq_init(mq_t *mq);
18  *   void mq_fini(mq_t *mq);
19  *   unsigned mq_count(mq_t *mq);
20  *   mq_msg_t *mq_tryget(mq_t *mq);
21  *   mq_msg_t *mq_get(mq_t *mq);
22  *   void mq_put(mq_t *mq, mq_msg_t *msg);
23  *
24  * The message queue linkage embedded in each message is to be treated as
25  * externally opaque (no need to initialize or clean up externally).  mq_fini()
26  * does not perform any cleanup of messages, since it knows nothing of their
27  * payloads.
28  */
29 #define mq_msg(a_mq_msg_type)	ql_elm(a_mq_msg_type)
30 
31 #define mq_gen(a_attr, a_prefix, a_mq_type, a_mq_msg_type, a_field)	\
32 typedef struct {							\
33 	mtx_t			lock;					\
34 	ql_head(a_mq_msg_type)	msgs;					\
35 	unsigned		count;					\
36 } a_mq_type;								\
37 a_attr bool								\
38 a_prefix##init(a_mq_type *mq) {						\
39 									\
40 	if (mtx_init(&mq->lock)) {					\
41 		return true;						\
42 	}								\
43 	ql_new(&mq->msgs);						\
44 	mq->count = 0;							\
45 	return false;							\
46 }									\
47 a_attr void								\
48 a_prefix##fini(a_mq_type *mq) {						\
49 	mtx_fini(&mq->lock);						\
50 }									\
51 a_attr unsigned								\
52 a_prefix##count(a_mq_type *mq) {					\
53 	unsigned count;							\
54 									\
55 	mtx_lock(&mq->lock);						\
56 	count = mq->count;						\
57 	mtx_unlock(&mq->lock);						\
58 	return count;							\
59 }									\
60 a_attr a_mq_msg_type *							\
61 a_prefix##tryget(a_mq_type *mq) {					\
62 	a_mq_msg_type *msg;						\
63 									\
64 	mtx_lock(&mq->lock);						\
65 	msg = ql_first(&mq->msgs);					\
66 	if (msg != NULL) {						\
67 		ql_head_remove(&mq->msgs, a_mq_msg_type, a_field);	\
68 		mq->count--;						\
69 	}								\
70 	mtx_unlock(&mq->lock);						\
71 	return msg;							\
72 }									\
73 a_attr a_mq_msg_type *							\
74 a_prefix##get(a_mq_type *mq) {						\
75 	a_mq_msg_type *msg;						\
76 	unsigned ns;							\
77 									\
78 	msg = a_prefix##tryget(mq);					\
79 	if (msg != NULL) {						\
80 		return msg;						\
81 	}								\
82 									\
83 	ns = 1;								\
84 	while (true) {							\
85 		mq_nanosleep(ns);					\
86 		msg = a_prefix##tryget(mq);				\
87 		if (msg != NULL) {					\
88 			return msg;					\
89 		}							\
90 		if (ns < 1000*1000*1000) {				\
91 			/* Double sleep time, up to max 1 second. */	\
92 			ns <<= 1;					\
93 			if (ns > 1000*1000*1000) {			\
94 				ns = 1000*1000*1000;			\
95 			}						\
96 		}							\
97 	}								\
98 }									\
99 a_attr void								\
100 a_prefix##put(a_mq_type *mq, a_mq_msg_type *msg) {			\
101 									\
102 	mtx_lock(&mq->lock);						\
103 	ql_elm_new(msg, a_field);					\
104 	ql_tail_insert(&mq->msgs, msg, a_field);			\
105 	mq->count++;							\
106 	mtx_unlock(&mq->lock);						\
107 }
108