Dynamic Load Balance 3.6.1+32-59d1
queue_template.h
Go to the documentation of this file.
1/*********************************************************************************/
2/* Copyright 2009-2024 Barcelona Supercomputing Center */
3/* */
4/* This file is part of the DLB library. */
5/* */
6/* DLB is free software: you can redistribute it and/or modify */
7/* it under the terms of the GNU Lesser General Public License as published by */
8/* the Free Software Foundation, either version 3 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* DLB is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU Lesser General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU Lesser General Public License */
17/* along with DLB. If not, see <https://www.gnu.org/licenses/>. */
18/*********************************************************************************/
19
20/* Credits: https://www.davidpriver.com/ctemplates.html */
21
22/* Include this header multiple times to implement a fixed-size circular queue.
23 * Before inclusion define at least QUEUE_T and QUEUE_SIZE to the type the
24 * queue can hold.
25 *
26 * QUEUE_KEY_T can be defined to set the type of the primary key, which should
27 * be either a subtype of QUEUE_T if using structs, or QUEUE_T.
28 */
29
30#ifndef QUEUE_TEMPLATE_H
31#define QUEUE_TEMPLATE_H
32
33#include "support/debug.h"
34
35#include <stdbool.h>
36#include <stdlib.h>
37#include <string.h>
38
39#define QUEUE_IMPL(word) QUEUE_COMB1(QUEUE_PREFIX,word)
40#define QUEUE_COMB1(pre, word) QUEUE_COMB2(pre, word)
41#define QUEUE_COMB2(pre, word) pre##word
42
43#endif /* QUEUE_TEMPLATE_H */
44
45#ifndef QUEUE_T
46#error "QUEUE_T must be defined"
47#endif
48
49#ifndef QUEUE_SIZE
50#error "QUEUE_SIZE must be defined"
51#endif
52
53#ifndef QUEUE_KEY_T
54#define QUEUE_KEY_T QUEUE_T
55#endif
56
57#define QUEUE_NAME QUEUE_COMB1(QUEUE_COMB1(queue,_), QUEUE_T)
58#define QUEUE_PREFIX QUEUE_COMB1(QUEUE_NAME, _)
59
60#define QUEUE_init QUEUE_IMPL(init)
61#define QUEUE_clear QUEUE_IMPL(clear)
62#define QUEUE_size QUEUE_IMPL(size)
63#define QUEUE_empty QUEUE_IMPL(empty)
64#define QUEUE_has_space QUEUE_IMPL(has_space)
65#define QUEUE_front QUEUE_IMPL(front)
66#define QUEUE_back QUEUE_IMPL(back)
67#define QUEUE_next QUEUE_IMPL(next)
68#define QUEUE_enqueue QUEUE_IMPL(enqueue)
69#define QUEUE_dequeue QUEUE_IMPL(dequeue)
70#define QUEUE_delete QUEUE_IMPL(delete)
71#define QUEUE_remove QUEUE_IMPL(remove)
72#define QUEUE_replace QUEUE_IMPL(replace)
73#define QUEUE_search QUEUE_IMPL(search)
74
75/* Queue template:
76 * - the actual capacity is QUEUE_SIZE - 1 because we don't save rear, but next. FIXME
77 * (it simplifies some functions a little bit)
78 * - 'front' points to the first element
79 * - 'next' points to the next spot where to push
80 */
81
82typedef struct QUEUE_NAME {
84 size_t front; /* position to the first element */
85 size_t next; /* position to the last empty spot */
86 size_t capacity;
88
89
90/* Initializers */
91
92static inline void QUEUE_init(QUEUE_NAME *queue) {
93 *queue = (const QUEUE_NAME) {
94 .capacity = QUEUE_SIZE, /* mainly for debugging */
95 };
96}
97
98static inline void QUEUE_clear(QUEUE_NAME *queue) {
99 queue->front = 0;
100 queue->next = 0;
101}
102
103
104/* Capacity */
105
106static inline size_t QUEUE_size(const QUEUE_NAME *queue) {
107 return queue->next >= queue->front
108 ? queue->next - queue->front
109 : QUEUE_SIZE - queue->front + queue->next;
110}
111
112static inline bool QUEUE_empty(const QUEUE_NAME *queue) {
113 return queue->front == queue->next;
114}
115
116static inline bool QUEUE_has_space(const QUEUE_NAME *queue) {
117 return (queue->next + 1) % QUEUE_SIZE != queue->front;
118}
119
120
121/* Element access */
122
123static inline QUEUE_T* QUEUE_front(QUEUE_NAME *queue) {
124 return QUEUE_empty(queue) ? NULL : &queue->items[queue->front];
125}
126
127static inline QUEUE_T* QUEUE_back(QUEUE_NAME *queue) {
128 size_t rear = queue->next > 0 ? queue->next-1 : QUEUE_SIZE-1;
129 return QUEUE_empty(queue) ? NULL : &queue->items[rear];
130}
131
132static inline QUEUE_T* QUEUE_next(QUEUE_NAME *queue, QUEUE_T *item) {
133 if (QUEUE_empty(queue) ) {
134 return NULL;
135 }
136 else if (queue->next > queue->front) {
137 /* queue is linear */
138 size_t next_index = item + 1 - queue->items;
139 return next_index < queue->next ? item + 1 : NULL;
140 }
141 else {
142 /* queue is circular */
143 size_t next_index = item + 1 < queue->items + QUEUE_SIZE
144 ? item + 1 - queue->items
145 : 0;
146 return next_index > queue->front || next_index < queue->next
147 ? &queue->items[next_index] : NULL;
148 }
149}
150
151
152/* Modifiers */
153
154/* Enqueue item to the back of the queue */
155static inline int QUEUE_enqueue(QUEUE_NAME *queue, QUEUE_T item) {
156 size_t new_next = (queue->next + 1) % QUEUE_SIZE;
157 if (unlikely(new_next == queue->front)) {
158 return -1;
159 }
160 queue->items[queue->next] = item;
161 queue->next = new_next;
162 return 0;
163}
164
165/* Dequeue item from the front of the queue */
166static inline int QUEUE_dequeue(QUEUE_NAME *queue, QUEUE_T *item) {
167 if (QUEUE_empty(queue)) {
168 return -1;
169 }
170 *item = queue->items[queue->front];
171 queue->front = (queue->front + 1) % QUEUE_SIZE;
172 return 0;
173}
174
175/* Delete element pointed by argument */
176static inline void QUEUE_delete(QUEUE_NAME *queue, const QUEUE_T *item) {
177 if (QUEUE_empty(queue)) {
178 return;
179 }
180
181 if (item == &queue->items[queue->front]) {
182 /* item is first element, just advance front */
183 queue->front = (queue->front + 1) % QUEUE_SIZE;
184 return;
185 }
186
187 size_t array_index = item - queue->items;
188
189 // FIXME: more efficient?
190 if (unlikely(
191 /* queue is linear */
192 (queue->next > queue->front
193 && (array_index < queue->front || array_index >= queue->next))
194 ||
195 /* queue is circular */
196 (queue->next <= queue->front
197 && (array_index >= queue->next && array_index < queue->front))
198 )) {
199 /* out of bounds */
200 return;
201 }
202
203 size_t write_index = array_index;
204 for (size_t read_index = (array_index + 1) % QUEUE_SIZE;
205 read_index != queue->next;
206 read_index = (read_index + 1) % QUEUE_SIZE) {
207 /* move items forward in the queue */
208 queue->items[write_index] = queue->items[read_index];
209 write_index = (write_index + 1) % QUEUE_SIZE;
210 }
211 queue->next = write_index;
212}
213
214/* Remove all items that match with key */
215static inline void QUEUE_remove(QUEUE_NAME *queue, QUEUE_KEY_T key) {
216
217 /* advance front for every first item that matches key */
218 while (!QUEUE_empty(queue)
219 && memcmp(&queue->items[queue->front], &key, sizeof(QUEUE_KEY_T)) == 0) {
220 queue->front = (queue->front + 1) % QUEUE_SIZE;
221 }
222
223 if (QUEUE_empty(queue)) {
224 return;
225 }
226
227 /* front does not match, iterate from 2nd item */
228 size_t write_index = (queue->front + 1) % QUEUE_SIZE;
229 for (size_t read_index = write_index;
230 read_index != queue->next;
231 read_index = (read_index + 1) % QUEUE_SIZE) {
232 if (read_index != write_index) {
233 /* move items forward in the queue */
234 queue->items[write_index] = queue->items[read_index];
235 }
236 if (memcmp(&queue->items[read_index], &key, sizeof(QUEUE_KEY_T)) != 0) {
237 /* write_index only advances when element is not to be removed */
238 write_index = (write_index + 1) % QUEUE_SIZE;
239 }
240 }
241 queue->next = write_index;
242}
243
244/* Replace the first item that matches by key */
245static inline void QUEUE_replace(QUEUE_NAME *queue, QUEUE_T item) {
246 for (size_t index = queue->front;
247 index != queue->next;
248 index = (index + 1) % QUEUE_SIZE) {
249 if (memcmp(&queue->items[index], &item, sizeof(QUEUE_KEY_T)) == 0) {
250 queue->items[index] = item;
251 return;
252 }
253 }
254}
255
256/* Return reference to the first item that matches by key */
257static inline QUEUE_T* QUEUE_search(QUEUE_NAME *queue, QUEUE_KEY_T key) {
258 for (size_t index = queue->front;
259 index != queue->next;
260 index = (index + 1) % QUEUE_SIZE) {
261 if (memcmp(&queue->items[index], &key, sizeof(QUEUE_KEY_T)) == 0) {
262 return &queue->items[index];
263 }
264 }
265 return NULL;
266}
267
268#undef QUEUE_T
269#undef QUEUE_SIZE
270#undef QUEUE_KEY_T
271#undef QUEUE_PREFIX
272#undef QUEUE_NAME
273#undef QUEUE_init
274#undef QUEUE_clear
275#undef QUEUE_size
276#undef QUEUE_empty
277#undef QUEUE_has_space
278#undef QUEUE_front
279#undef QUEUE_back
280#undef QUEUE_next
281#undef QUEUE_enqueue
282#undef QUEUE_dequeue
283#undef QUEUE_delete
284#undef QUEUE_remove
285#undef QUEUE_replace
286#undef QUEUE_search
#define unlikely(expr)
Definition: dlb_common.h:29
#define QUEUE_replace
Definition: queue_template.h:72
#define QUEUE_search
Definition: queue_template.h:73
#define QUEUE_has_space
Definition: queue_template.h:64
#define QUEUE_dequeue
Definition: queue_template.h:69
#define QUEUE_enqueue
Definition: queue_template.h:68
#define QUEUE_front
Definition: queue_template.h:65
#define QUEUE_next
Definition: queue_template.h:67
#define QUEUE_back
Definition: queue_template.h:66
#define QUEUE_clear
Definition: queue_template.h:61
#define QUEUE_init
Definition: queue_template.h:60
#define QUEUE_size
Definition: queue_template.h:62
#define QUEUE_empty
Definition: queue_template.h:63
#define QUEUE_KEY_T
Definition: queue_template.h:54
#define QUEUE_NAME
Definition: queue_template.h:57
#define QUEUE_delete
Definition: queue_template.h:70
#define QUEUE_remove
Definition: queue_template.h:71
@ QUEUE_SIZE
Definition: shmem_async.c:38
#define QUEUE_T
Definition: shmem_cpuinfo.c:63
Definition: queue_template.h:82
size_t front
Definition: queue_template.h:84
QUEUE_T items[QUEUE_SIZE]
Definition: queue_template.h:83
size_t capacity
Definition: queue_template.h:86
size_t next
Definition: queue_template.h:85