The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
fifo.c
Go to the documentation of this file.
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 {
48  unsigned int i;
49 
50  if (fi->free_node) {
51  for (i = 0 ; i < fi->num; i++) {
52  unsigned int element;
53 
54  element = i + fi->first;
55  if (element > fi->max) {
56  element -= fi->max;
57  }
58 
59  fi->free_node(fi->data[element]);
60  fi->data[element] = NULL;
61  }
62  }
63 
64  memset(fi, 0, sizeof(*fi));
65 
66  return 0;
67 }
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 {
87  fr_fifo_t *fi;
88 
89  if ((max < 2) || (max > (1024 * 1024))) return NULL;
90 
91  fi = talloc_zero_size(ctx, (sizeof(*fi) + (sizeof(fi->data[0])*max)));
92  if (!fi) return NULL;
93  talloc_set_type(fi, fr_fifo_t);
94  talloc_set_destructor(fi, _fifo_free);
95 
96  fi->max = max;
97  fi->type = type;
98  fi->free_node = free_node;
99 
100  return fi;
101 }
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 {
113  if (!fi || !data) return -1;
114 
115  if (fi->num >= fi->max) return -1;
116 
117 #ifndef TALLOC_GET_TYPE_ABORT_NOOP
118  if (fi->type) _talloc_get_type_abort(data, fi->type, __location__);
119 #endif
120 
121  fi->data[fi->last++] = data;
122  if (fi->last >= fi->max) fi->last = 0;
123  fi->num++;
124 
125  return 0;
126 }
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  */
136 {
137  void *data;
138 
139  if (!fi || (fi->num == 0)) return NULL;
140 
141  data = fi->data[fi->first++];
142 
143  if (fi->first >= fi->max) {
144  fi->first = 0;
145  }
146  fi->num--;
147 
148  return data;
149 }
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  */
159 {
160  if (!fi || (fi->num == 0)) return NULL;
161 
162  return fi->data[fi->first];
163 }
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  */
171 {
172  if (!fi) return 0;
173 
174  return fi->num;
175 }
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
#define RCSID(id)
Definition: build.h:481
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition: debug.h:228
int main(int argc, char **argv)
Definition: dhcpclient.c:521
void * fr_fifo_peek(fr_fifo_t *fi)
Examine the next element that would be popped.
Definition: fifo.c:158
char const * type
Type of elements.
Definition: fifo.c:36
unsigned int last
Head and tail indexes for the fifo.
Definition: fifo.c:32
void * data[1]
Definition: fifo.c:38
fr_fifo_t * _fr_fifo_create(TALLOC_CTX *ctx, char const *type, int max, fr_fifo_free_t free_node)
Create a fifo queue.
Definition: fifo.c:85
unsigned int num
How many elements exist in the fifo.
Definition: fifo.c:31
unsigned int fr_fifo_num_elements(fr_fifo_t *fi)
Return the number of elements in the fifo queue.
Definition: fifo.c:170
void * fr_fifo_pop(fr_fifo_t *fi)
Pop data off of the fifo.
Definition: fifo.c:135
int fr_fifo_push(fr_fifo_t *fi, void *data)
Push data onto the fifo.
Definition: fifo.c:111
fr_fifo_free_t free_node
Function to call to free nodes when the fifo is freed.
Definition: fifo.c:34
unsigned int max
How many elements were created in the fifo.
Definition: fifo.c:33
unsigned int first
Definition: fifo.c:32
static int _fifo_free(fr_fifo_t *fi)
Free a fifo and optionally, any data still enqueued.
Definition: fifo.c:46
Definition: fifo.c:30
#define fr_fifo_create(_ctx, _max_entries, _node_free)
Creates a fifo.
Definition: fifo.h:66
void(* fr_fifo_free_t)(void *)
Definition: fifo.h:36
talloc_free(reap)
static size_t array[MY_ARRAY_SIZE]
fr_aka_sim_id_type_t type
static fr_slen_t data
Definition: value.h:1265