The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
fring.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 /** 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  */
37  void const *end; //!< End of allocated memory
38 
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 {
57  void *next;
58 
59  /*
60  * Free any data left in the buffer
61  */
62  while ((next = fr_fring_next(fring))) talloc_free(next);
63 
64  if (fring->lock) pthread_mutex_destroy(&fring->mutex);
65 
66  return 0;
67 }
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 {
80  fr_fring_t *fring;
81 
82  uint32_t pow;
83 
84  /*
85  * Find the nearest power of 2 (rounding up)
86  */
87  for (pow = 0x00000001;
88  pow < size;
89  pow <<= 1);
90  size = pow;
91  size--;
92 
93  fring = talloc_zero(ctx, fr_fring_t);
94  if (!fring) return NULL;
95  talloc_set_destructor(fring, _fring_free);
96 
97  fring->data = talloc_zero_array(fring, void *, size);
98  if (!fring->data) {
99  talloc_free(fring);
100  return NULL;
101  }
102  fring->size = size;
103 
104  if (lock) {
105  fring->lock = true;
106  pthread_mutex_init(&fring->mutex, NULL);
107  }
108 
109  return fring;
110 }
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 {
122  bool freed = false;
123  if (fring->lock) pthread_mutex_lock(&fring->mutex);
124 
125  if (fring->data[fring->in]) {
126  freed = true;
127  talloc_free(fring->data[fring->in]);
128  }
129 
130  fring->data[fring->in] = in;
131  fring->in = (fring->in + 1) & fring->size;
132 
133  /* overwrite - out is advanced ahead of in */
134  if (fring->in == fring->out) fring->out = (fring->out + 1) & fring->size;
135 
136  if (fring->lock) pthread_mutex_unlock(&fring->mutex);
137 
138  return freed ? 1 : 0;
139 }
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 {
151  if (fring->lock) pthread_mutex_lock(&fring->mutex);
152 
153  if (fring->data[fring->in]) {
154  if (fring->lock) pthread_mutex_unlock(&fring->mutex);
155 
156  return -1;
157  }
158 
159  fring->data[fring->in] = in;
160  fring->in = (fring->in + 1) & fring->size;
161 
162  /* overwrite - out is advanced ahead of in */
163  if (fring->in == fring->out) fring->out = (fring->out + 1) & fring->size;
164 
165  if (fring->lock) pthread_mutex_unlock(&fring->mutex);
166 
167  return 0;
168 }
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  */
178 {
179  void *out = NULL;
180 
181  if (fring->lock) pthread_mutex_lock(&fring->mutex);
182 
183  /* Buffer is empty */
184  if (fring->out == fring->in) goto done;
185 
186  out = fring->data[fring->out];
187  fring->data[fring->out] = NULL;
188  fring->out = (fring->out + 1) & fring->size;
189 
190 done:
191  if (fring->lock) pthread_mutex_unlock(&fring->mutex);
192 
193  return out;
194 }
#define RCSID(id)
Definition: build.h:444
static fr_slen_t in
Definition: dict.h:645
void const * end
End of allocated memory.
Definition: fring.c:37
uint32_t size
Definition: fring.c:39
bool lock
Perform thread synchronisation.
Definition: fring.c:45
void * fr_fring_next(fr_fring_t *fring)
Remove an item from the buffer.
Definition: fring.c:177
uint32_t in
Write index.
Definition: fring.c:40
void ** data
Ring buffer data.
Definition: fring.c:43
fr_fring_t * fr_fring_alloc(TALLOC_CTX *ctx, uint32_t size, bool lock)
Initialise a ring buffer with fixed element size.
Definition: fring.c:78
int fr_fring_overwrite(fr_fring_t *fring, void *in)
Insert a new item into the circular buffer, freeing the tail if we hit it.
Definition: fring.c:120
pthread_mutex_t mutex
Thread synchronisation mutex.
Definition: fring.c:47
int fr_fring_insert(fr_fring_t *fring, void *in)
Insert a new item into the circular buffer if the buffer is not full.
Definition: fring.c:149
uint32_t out
Read index.
Definition: fring.c:41
static int _fring_free(fr_fring_t *fring)
Destroy mutex associated with ring buffer.
Definition: fring.c:55
Standard thread safe circular buffer.
Definition: fring.c:36
talloc_free(reap)
unsigned int uint32_t
Definition: merged_model.c:33
static bool done
Definition: radclient.c:80
if(!subtype_vp) goto fail
static size_t char ** out
Definition: value.h:984