/src/freeradius-server/src/lib/io/queue.c
Line | Count | Source |
1 | | /* |
2 | | * This program is free software; you can redistribute it and/or modify |
3 | | * it under the terms of the GNU General Public License as published by |
4 | | * the Free Software Foundation; either version 2 of the License, or |
5 | | * (at your option) any later version. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** |
18 | | * $Id: 64c0a88590e7434d42efe713533db00f285d24b7 $ |
19 | | * |
20 | | * @brief Thread-unsafe queues |
21 | | * @file io/queue.c |
22 | | * |
23 | | * @copyright 2016 Alan DeKok (aland@freeradius.org) |
24 | | */ |
25 | | RCSID("$Id: 64c0a88590e7434d42efe713533db00f285d24b7 $") |
26 | | |
27 | | #include <stdint.h> |
28 | | #include <string.h> |
29 | | |
30 | | #include <freeradius-devel/util/debug.h> |
31 | | #include <freeradius-devel/io/queue.h> |
32 | | |
33 | | struct fr_queue_s { |
34 | | int head; //!< head of the queue |
35 | | int tail; //!< tail of the queue |
36 | | |
37 | | int size; //!< size of the queue |
38 | | int num; //!< number of elements pushed into the queue |
39 | | |
40 | | void *entry[1]; //!< Array of queue data. |
41 | | }; |
42 | | |
43 | | /** Create a non-thread-safe queue. |
44 | | * |
45 | | * @param[in] ctx the talloc ctx |
46 | | * @param[in] size the number of entries in the queue |
47 | | * @return |
48 | | * - NULL on error |
49 | | * - fr_queue_t *, a pointer to the allocated and initialized queue |
50 | | */ |
51 | | fr_queue_t *fr_queue_create(TALLOC_CTX *ctx, int size) |
52 | 0 | { |
53 | 0 | fr_queue_t *fq; |
54 | |
|
55 | 0 | if (size <= 0) return NULL; |
56 | | |
57 | | /* |
58 | | * Allocate a contiguous blob for the header and queue. |
59 | | * This helps with memory locality. |
60 | | * |
61 | | * Since we're allocating a blob, we should also set the |
62 | | * name of the data, too. |
63 | | */ |
64 | 0 | fq = talloc_size(ctx, sizeof(*fq) + (size - 1) * sizeof(fq->entry[0])); |
65 | 0 | if (!fq) return NULL; |
66 | | |
67 | 0 | talloc_set_name_const(fq, "fr_queue_t"); |
68 | |
|
69 | 0 | memset(fq, 0, sizeof(*fq) + (size - 1) * sizeof(fq->entry[0])); |
70 | |
|
71 | 0 | fq->size = size; |
72 | |
|
73 | 0 | return fq; |
74 | 0 | } |
75 | | |
76 | | |
77 | | /** Push a pointer into the queue |
78 | | * |
79 | | * @param[in] fq the queue |
80 | | * @param[in] data the data to push |
81 | | * @return |
82 | | * - true on successful push |
83 | | * - false on queue full |
84 | | */ |
85 | | bool fr_queue_push(fr_queue_t *fq, void *data) |
86 | 0 | { |
87 | 0 | (void) talloc_get_type_abort(fq, fr_queue_t); |
88 | |
|
89 | 0 | if (fq->num >= fq->size) return false; |
90 | | |
91 | 0 | fq->entry[fq->head++] = data; |
92 | 0 | if (fq->head >= fq->size) fq->head = 0; |
93 | 0 | fq->num++; |
94 | |
|
95 | 0 | return true; |
96 | 0 | } |
97 | | |
98 | | |
99 | | /** Pop a pointer from the queue |
100 | | * |
101 | | * @param[in] fq the queue |
102 | | * @param[in] p_data where to write the data |
103 | | * @return |
104 | | * - true on successful pop |
105 | | * - false on queue empty |
106 | | */ |
107 | | bool fr_queue_pop(fr_queue_t *fq, void **p_data) |
108 | 0 | { |
109 | 0 | (void) talloc_get_type_abort(fq, fr_queue_t); |
110 | |
|
111 | 0 | if (fq->num == 0) return false; |
112 | | |
113 | 0 | *p_data = fq->entry[fq->tail++]; |
114 | 0 | if (fq->tail >= fq->size) fq->tail = 0; |
115 | 0 | fq->num--; |
116 | |
|
117 | 0 | return true; |
118 | 0 | } |
119 | | |
120 | | |
121 | | /** get the size of a queue |
122 | | * |
123 | | * @param[in] fq the queue |
124 | | * @return |
125 | | * - The size of the queue. |
126 | | */ |
127 | | int fr_queue_size(fr_queue_t *fq) |
128 | 0 | { |
129 | 0 | (void) talloc_get_type_abort(fq, fr_queue_t); |
130 | |
|
131 | 0 | return fq->size; |
132 | 0 | } |
133 | | |
134 | | |
135 | | /** get the number of elements in a queue. |
136 | | * |
137 | | * @param[in] fq the queue |
138 | | * @return |
139 | | * - The number of elements in the queue. |
140 | | */ |
141 | | int fr_queue_num_elements(fr_queue_t *fq) |
142 | 0 | { |
143 | 0 | (void) talloc_get_type_abort(fq, fr_queue_t); |
144 | |
|
145 | 0 | return fq->num; |
146 | 0 | } |
147 | | |
148 | | |
149 | | |
150 | | /** Resize a queue, and copy the entries over. |
151 | | * |
152 | | * @param[in] fq the queue |
153 | | * @param[in] size the new size of the queue |
154 | | * @return |
155 | | * - NULL on error |
156 | | * - fr_queue_t * the new queue, which MAY BE fq. |
157 | | */ |
158 | | fr_queue_t *fr_queue_resize(fr_queue_t *fq, int size) |
159 | 0 | { |
160 | 0 | fr_queue_t *nq; |
161 | 0 | TALLOC_CTX *ctx; |
162 | |
|
163 | 0 | (void) talloc_get_type_abort(fq, fr_queue_t); |
164 | |
|
165 | 0 | if (size <= 0) return NULL; |
166 | | |
167 | 0 | if (size <= fq->size) return fq; |
168 | | |
169 | 0 | ctx = talloc_parent(fq); |
170 | | |
171 | | /* |
172 | | * If we can't create the new queue, return the old one. |
173 | | */ |
174 | 0 | nq = fr_queue_create(ctx, size); |
175 | 0 | if (!nq) return fq; |
176 | | |
177 | | /* |
178 | | * Empty: we're done. |
179 | | */ |
180 | 0 | if (!fq->num) { |
181 | 0 | goto done; |
182 | 0 | } |
183 | | |
184 | | /* |
185 | | * Simple block of used elements, copy it. |
186 | | */ |
187 | 0 | if (fq->head > fq->tail) { |
188 | 0 | fr_assert(fq->num == (fq->head - fq->tail)); |
189 | 0 | memcpy(&nq->entry[0], &fq->entry[fq->tail], |
190 | 0 | (uint8_t *) &fq->entry[fq->head] - (uint8_t *) &fq->entry[fq->tail]); |
191 | 0 | nq->head = fq->num; |
192 | 0 | nq->num = fq->num; |
193 | 0 | goto done; |
194 | 0 | } |
195 | | |
196 | | /* |
197 | | * The block of elements is split in two. Copy the tail |
198 | | * to the bottom of our array, and then then head. |
199 | | */ |
200 | 0 | memcpy(&nq->entry[0], &fq->entry[fq->tail], |
201 | 0 | (uint8_t *) &fq->entry[fq->size] - (uint8_t *) &fq->entry[fq->tail]); |
202 | 0 | nq->head = fq->size - fq->tail; |
203 | |
|
204 | 0 | fr_assert((nq->head + fq->head) == fq->num); |
205 | |
|
206 | 0 | memcpy(&nq->entry[nq->head], &fq->entry[0], |
207 | 0 | (uint8_t *) &fq->entry[fq->head] - (uint8_t *) &fq->entry[0]); |
208 | 0 | nq->head = fq->num; |
209 | 0 | nq->num = fq->num; |
210 | |
|
211 | 0 | done: |
212 | 0 | talloc_free(fq); |
213 | |
|
214 | 0 | return nq; |
215 | 0 | } |
216 | | |
217 | | |
218 | | /** Pull all entries from an atomic queue into our local queue. |
219 | | * |
220 | | * @param[in] fq the local queue |
221 | | * @param[in] aq the atomic queue |
222 | | * @return |
223 | | * - number of entries successfully moved over |
224 | | */ |
225 | | int fr_queue_localize_atomic(fr_queue_t *fq, fr_atomic_queue_t *aq) |
226 | 0 | { |
227 | 0 | void *data; |
228 | 0 | int i, room; |
229 | |
|
230 | 0 | (void) talloc_get_type_abort(fq, fr_queue_t); |
231 | | |
232 | | /* |
233 | | * No room to push anything, return an error. |
234 | | */ |
235 | 0 | room = fq->size - fq->num; |
236 | 0 | if (!room) return 0; |
237 | | |
238 | | /* |
239 | | * Pop as many entries as we have room for. |
240 | | */ |
241 | 0 | for (i = 0; i < room; i++) { |
242 | 0 | if (!fr_atomic_queue_pop(aq, &data)) { |
243 | 0 | return i; |
244 | 0 | } |
245 | | |
246 | 0 | fq->entry[fq->head++] = data; |
247 | 0 | if (fq->head >= fq->size) fq->head = 0; |
248 | 0 | fq->num++; |
249 | 0 | fr_assert(fq->num <= fq->size); |
250 | 0 | } |
251 | | |
252 | 0 | return room; |
253 | 0 | } |
254 | | |
255 | | #ifndef NDEBUG |
256 | | /** Dump a queue. |
257 | | * |
258 | | * @param[in] fp where the debugging information will be printed. |
259 | | * @param[in] fq the queue |
260 | | */ |
261 | | void fr_queue_debug(FILE *fp, fr_queue_t *fq) |
262 | 0 | { |
263 | 0 | int i; |
264 | |
|
265 | 0 | fprintf(fp, "FQ %p size %d, head %d, tail %d\n", |
266 | 0 | fq, fq->size, fq->head, fq->tail); |
267 | |
|
268 | 0 | for (i = 0; i < fq->size; i++) { |
269 | 0 | fprintf(fp, "\t[%d] = { %p }\n", |
270 | 0 | i, fq->entry[i]); |
271 | 0 | } |
272 | 0 | } |
273 | | #endif |