Coverage Report

Created: 2026-01-10 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/fring.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
/** Implementation of a circular buffer with fixed element size
18
 *
19
 * This offers similar functionality to ring_buffer.c, but uses a fixed
20
 * element size, and expects all elements to be talloced.
21
 *
22
 * @file src/lib/util/fring.c
23
 *
24
 * @copyright 2013 The FreeRADIUS server project
25
 * @copyright 2013 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
26
 */
27
RCSID("$Id: 9438d77eea3fafc3f2a3bb54cdc90a036fba9154 $")
28
29
#include <freeradius-devel/util/fring.h>
30
31
#include <pthread.h>
32
33
/** Standard thread safe circular buffer
34
 *
35
 */
36
struct fr_fring_buffer {
37
  void const    *end;     //!< End of allocated memory
38
39
  uint32_t    size;
40
  uint32_t    in;     //!< Write index
41
  uint32_t    out;      //!< Read index
42
43
  void      **data;     //!< Ring buffer data
44
45
  bool      lock;     //!< Perform thread synchronisation
46
47
  pthread_mutex_t   mutex;      //!< Thread synchronisation mutex
48
};
49
50
/** Destroy mutex associated with ring buffer
51
 *
52
 * @param[in] fring being freed.
53
 * @return 0
54
 */
55
static int _fring_free(fr_fring_t *fring)
56
0
{
57
0
  void *next;
58
59
  /*
60
   *  Free any data left in the buffer
61
   */
62
0
  while ((next = fr_fring_next(fring))) talloc_free(next);
63
64
0
  if (fring->lock) pthread_mutex_destroy(&fring->mutex);
65
66
0
  return 0;
67
0
}
68
69
/** Initialise a ring buffer with fixed element size
70
 *
71
 * @param[in] ctx to allocate the buffer in.
72
 * @param[in] size  of buffer to allocate.
73
 * @param[in] lock  If true, insert and next operations will lock the buffer.
74
 * @return
75
 *  - New fring.
76
 *  - NULL on error.
77
 */
78
fr_fring_t *fr_fring_alloc(TALLOC_CTX *ctx, uint32_t size, bool lock)
79
0
{
80
0
  fr_fring_t *fring;
81
82
0
  uint32_t pow;
83
84
  /*
85
   *  Find the nearest power of 2 (rounding up)
86
   */
87
0
  for (pow = 0x00000001;
88
0
       pow < size;
89
0
       pow <<= 1);
90
0
  size = pow;
91
0
  size--;
92
93
0
  fring = talloc_zero(ctx, fr_fring_t);
94
0
  if (!fring) return NULL;
95
0
  talloc_set_destructor(fring, _fring_free);
96
97
0
  fring->data = talloc_zero_array(fring, void *, size);
98
0
  if (!fring->data) {
99
0
    talloc_free(fring);
100
0
    return NULL;
101
0
  }
102
0
  fring->size = size;
103
104
0
  if (lock) {
105
0
    fring->lock = true;
106
0
    pthread_mutex_init(&fring->mutex, NULL);
107
0
  }
108
109
0
  return fring;
110
0
}
111
112
/** Insert a new item into the circular buffer, freeing the tail if we hit it
113
 *
114
 * @param[in] fring to insert item into
115
 * @param[in] in  item to insert (must have been allocated with talloc).
116
 * @return
117
 *  - 0 if we inserted the item without freeing existing items.
118
 *  - 1 if we inserted the item, but needed to free an existing item.
119
 */
120
int fr_fring_overwrite(fr_fring_t *fring, void *in)
121
0
{
122
0
  bool freed = false;
123
0
  if (fring->lock) pthread_mutex_lock(&fring->mutex);
124
125
0
  if (fring->data[fring->in]) {
126
0
    freed = true;
127
0
    talloc_free(fring->data[fring->in]);
128
0
  }
129
130
0
  fring->data[fring->in] = in;
131
0
  fring->in = (fring->in + 1) & fring->size;
132
133
  /* overwrite - out is advanced ahead of in */
134
0
  if (fring->in == fring->out) fring->out = (fring->out + 1) & fring->size;
135
136
0
  if (fring->lock) pthread_mutex_unlock(&fring->mutex);
137
138
0
  return freed ? 1 : 0;
139
0
}
140
141
/** Insert a new item into the circular buffer if the buffer is not full
142
 *
143
 * @param[in] fring to insert item into.
144
 * @param[in] in  item to insert.
145
 * @return
146
 *  - 0 if we inserted the item.
147
 *  - -1 if there's no more space in the buffer to insert items
148
 */
149
int fr_fring_insert(fr_fring_t *fring, void *in)
150
0
{
151
0
  if (fring->lock) pthread_mutex_lock(&fring->mutex);
152
153
0
  if (fring->data[fring->in]) {
154
0
    if (fring->lock) pthread_mutex_unlock(&fring->mutex);
155
156
0
    return -1;
157
0
  }
158
159
0
  fring->data[fring->in] = in;
160
0
  fring->in = (fring->in + 1) & fring->size;
161
162
  /* overwrite - out is advanced ahead of in */
163
0
  if (fring->in == fring->out) fring->out = (fring->out + 1) & fring->size;
164
165
0
  if (fring->lock) pthread_mutex_unlock(&fring->mutex);
166
167
0
  return 0;
168
0
}
169
170
/** Remove an item from the buffer
171
 *
172
 * @param[in] fring to drain data from.
173
 * @return
174
 *  - NULL if no dataents in the buffer.
175
 *  - An dataent from the buffer reparented to ctx.
176
 */
177
void *fr_fring_next(fr_fring_t *fring)
178
0
{
179
0
  void *out = NULL;
180
181
0
  if (fring->lock) pthread_mutex_lock(&fring->mutex);
182
183
  /* Buffer is empty */
184
0
  if (fring->out == fring->in) goto done;
185
186
0
  out = fring->data[fring->out];
187
0
  fring->data[fring->out] = NULL;
188
0
  fring->out = (fring->out + 1) & fring->size;
189
190
0
done:
191
0
  if (fring->lock) pthread_mutex_unlock(&fring->mutex);
192
193
0
  return out;
194
0
}