/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 | } |