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