/src/freeradius-server/src/lib/util/fifo.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 | | /** Non-thread-safe fifo (FIFO) implementation |
18 | | * |
19 | | * @file src/lib/util/fifo.c |
20 | | * |
21 | | * @copyright 2005,2006 The FreeRADIUS server project |
22 | | * @copyright 2005 Alan DeKok (aland@freeradius.org) |
23 | | */ |
24 | | RCSID("$Id: 3c967c14f10148ad89679fcadadb49fb8f212808 $") |
25 | | |
26 | | #include <string.h> |
27 | | |
28 | | #include <freeradius-devel/util/fifo.h> |
29 | | |
30 | | struct fr_fifo_s { |
31 | | unsigned int num; //!< How many elements exist in the fifo. |
32 | | unsigned int first, last; //!< Head and tail indexes for the fifo. |
33 | | unsigned int max; //!< How many elements were created in the fifo. |
34 | | fr_fifo_free_t free_node; //!< Function to call to free nodes when the fifo is freed. |
35 | | |
36 | | char const *type; //!< Type of elements. |
37 | | |
38 | | void *data[1]; |
39 | | }; |
40 | | |
41 | | /** Free a fifo and optionally, any data still enqueued |
42 | | * |
43 | | * @param[in] fi to free. |
44 | | * @return 0 |
45 | | */ |
46 | | static int _fifo_free(fr_fifo_t *fi) |
47 | 0 | { |
48 | 0 | unsigned int i; |
49 | |
|
50 | 0 | if (fi->free_node) { |
51 | 0 | for (i = 0 ; i < fi->num; i++) { |
52 | 0 | unsigned int element; |
53 | |
|
54 | 0 | element = i + fi->first; |
55 | 0 | if (element > fi->max) { |
56 | 0 | element -= fi->max; |
57 | 0 | } |
58 | |
|
59 | 0 | fi->free_node(fi->data[element]); |
60 | 0 | fi->data[element] = NULL; |
61 | 0 | } |
62 | 0 | } |
63 | |
|
64 | 0 | memset(fi, 0, sizeof(*fi)); |
65 | |
|
66 | 0 | return 0; |
67 | 0 | } |
68 | | |
69 | | /** Create a fifo queue |
70 | | * |
71 | | * The first element enqueued will be the first to be dequeued. |
72 | | * |
73 | | * @note The created fifo does not provide any thread synchronisation functionality |
74 | | * such as mutexes. If multiple threads are enqueueing and dequeueing data |
75 | | * the callers must synchronise their access. |
76 | | * |
77 | | * @param[in] ctx to allocate fifo array in. |
78 | | * @param[in] type Talloc type of elements (may be NULL). |
79 | | * @param[in] max The maximum number of elements allowed. |
80 | | * @param[in] free_node Function to use to free node data if the fifo is freed. |
81 | | * @return |
82 | | * - A new fifo queue. |
83 | | * - NULL on error. |
84 | | */ |
85 | | fr_fifo_t *_fr_fifo_create(TALLOC_CTX *ctx, char const *type, int max, fr_fifo_free_t free_node) |
86 | 0 | { |
87 | 0 | fr_fifo_t *fi; |
88 | |
|
89 | 0 | if ((max < 2) || (max > (1024 * 1024))) return NULL; |
90 | | |
91 | 0 | fi = talloc_zero_size(ctx, (sizeof(*fi) + (sizeof(fi->data[0])*max))); |
92 | 0 | if (!fi) return NULL; |
93 | 0 | talloc_set_type(fi, fr_fifo_t); |
94 | 0 | talloc_set_destructor(fi, _fifo_free); |
95 | |
|
96 | 0 | fi->max = max; |
97 | 0 | fi->type = type; |
98 | 0 | fi->free_node = free_node; |
99 | |
|
100 | 0 | return fi; |
101 | 0 | } |
102 | | |
103 | | /** Push data onto the fifo |
104 | | * |
105 | | * @param[in] fi FIFO to push data onto. |
106 | | * @param[in] data to push. |
107 | | * @return |
108 | | * - 0 on success. |
109 | | * - -1 on error. |
110 | | */ |
111 | | int fr_fifo_push(fr_fifo_t *fi, void *data) |
112 | 0 | { |
113 | 0 | if (!fi || !data) return -1; |
114 | | |
115 | 0 | if (fi->num >= fi->max) return -1; |
116 | | |
117 | 0 | #ifndef TALLOC_GET_TYPE_ABORT_NOOP |
118 | 0 | if (fi->type) _talloc_get_type_abort(data, fi->type, __location__); |
119 | 0 | #endif |
120 | |
|
121 | 0 | fi->data[fi->last++] = data; |
122 | 0 | if (fi->last >= fi->max) fi->last = 0; |
123 | 0 | fi->num++; |
124 | |
|
125 | 0 | return 0; |
126 | 0 | } |
127 | | |
128 | | /** Pop data off of the fifo |
129 | | * |
130 | | * @param[in] fi FIFO to pop data from. |
131 | | * @return |
132 | | * - The data popped. |
133 | | * - NULL if the queue is empty. |
134 | | */ |
135 | | void *fr_fifo_pop(fr_fifo_t *fi) |
136 | 0 | { |
137 | 0 | void *data; |
138 | |
|
139 | 0 | if (!fi || (fi->num == 0)) return NULL; |
140 | | |
141 | 0 | data = fi->data[fi->first++]; |
142 | |
|
143 | 0 | if (fi->first >= fi->max) { |
144 | 0 | fi->first = 0; |
145 | 0 | } |
146 | 0 | fi->num--; |
147 | |
|
148 | 0 | return data; |
149 | 0 | } |
150 | | |
151 | | /** Examine the next element that would be popped |
152 | | * |
153 | | * @param[in] fi FIFO to peek at. |
154 | | * @return |
155 | | * - The data at the head of the queue |
156 | | * - NULL if the queue is empty. |
157 | | */ |
158 | | void *fr_fifo_peek(fr_fifo_t *fi) |
159 | 0 | { |
160 | 0 | if (!fi || (fi->num == 0)) return NULL; |
161 | | |
162 | 0 | return fi->data[fi->first]; |
163 | 0 | } |
164 | | |
165 | | /** Return the number of elements in the fifo queue |
166 | | * |
167 | | * @param[in] fi FIFO to count elements in. |
168 | | * @return the number of elements |
169 | | */ |
170 | | unsigned int fr_fifo_num_elements(fr_fifo_t *fi) |
171 | 0 | { |
172 | 0 | if (!fi) return 0; |
173 | | |
174 | 0 | return fi->num; |
175 | 0 | } |
176 | | |
177 | | #ifdef TESTING |
178 | | |
179 | | /* |
180 | | * cc -DTESTING -I .. fifo.c -o fifo |
181 | | * |
182 | | * ./fifo |
183 | | */ |
184 | | |
185 | | #define MAX 1024 |
186 | | int main(int argc, char **argv) |
187 | | { |
188 | | int i, j, array[MAX]; |
189 | | fr_fifo_t *fi; |
190 | | |
191 | | fi = fr_fifo_create(NULL, MAX, NULL); |
192 | | if (!fi) fr_exit(1); |
193 | | |
194 | | for (j = 0; j < 5; j++) { |
195 | | #define SPLIT (MAX/3) |
196 | | #define COUNT ((j * SPLIT) + i) |
197 | | for (i = 0; i < SPLIT; i++) { |
198 | | array[COUNT % MAX] = COUNT; |
199 | | |
200 | | if (fr_fifo_push(fi, &array[COUNT % MAX]) < 0) { |
201 | | fprintf(stderr, "%d %d\tfailed pushing %d\n", |
202 | | j, i, COUNT); |
203 | | fr_exit(2); |
204 | | } |
205 | | |
206 | | if (fr_fifo_num_elements(fi) != (i + 1)) { |
207 | | fprintf(stderr, "%d %d\tgot size %d expected %d\n", |
208 | | j, i, i + 1, fr_fifo_num_elements(fi)); |
209 | | fr_exit(1); |
210 | | } |
211 | | } |
212 | | |
213 | | if (fr_fifo_num_elements(fi) != SPLIT) { |
214 | | fprintf(stderr, "HALF %d %d\n", |
215 | | fr_fifo_num_elements(fi), SPLIT); |
216 | | fr_exit(1); |
217 | | } |
218 | | |
219 | | for (i = 0; i < SPLIT; i++) { |
220 | | int *p; |
221 | | |
222 | | p = fr_fifo_pop(fi); |
223 | | if (!p) { |
224 | | fprintf(stderr, "No pop at %d\n", i); |
225 | | fr_exit(3); |
226 | | } |
227 | | |
228 | | if (*p != COUNT) { |
229 | | fprintf(stderr, "%d %d\tgot %d expected %d\n", |
230 | | j, i, *p, COUNT); |
231 | | fr_exit(4); |
232 | | } |
233 | | |
234 | | if (fr_fifo_num_elements(fi) != SPLIT - (i + 1)) { |
235 | | fprintf(stderr, "%d %d\tgot size %d expected %d\n", |
236 | | j, i, SPLIT - (i + 1), fr_fifo_num_elements(fi)); |
237 | | fr_exit(1); |
238 | | } |
239 | | } |
240 | | |
241 | | if (fr_fifo_num_elements(fi) != 0) { |
242 | | fprintf(stderr, "ZERO %d %d\n", |
243 | | fr_fifo_num_elements(fi), 0); |
244 | | fr_exit(1); |
245 | | } |
246 | | } |
247 | | |
248 | | talloc_free(fi); |
249 | | |
250 | | fr_exit(0); |
251 | | } |
252 | | #endif |