Coverage Report

Created: 2026-04-12 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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