aboutsummaryrefslogtreecommitdiff
path: root/include/libwebsockets/lws-ring.h
blob: e642f0f24e9bbe34b6e5739723bf290e521cd331 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/*
 * libwebsockets - small server side websockets and web server implementation
 *
 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/** \defgroup lws_ring LWS Ringbuffer APIs
 * ##lws_ring: generic ringbuffer struct
 *
 * Provides an abstract ringbuffer api supporting one head and one or an
 * unlimited number of tails.
 *
 * All of the members are opaque and manipulated by lws_ring_...() apis.
 *
 * The lws_ring and its buffer is allocated at runtime on the heap, using
 *
 *  - lws_ring_create()
 *  - lws_ring_destroy()
 *
 * It may contain any type, the size of the "element" stored in the ring
 * buffer and the number of elements is given at creation time.
 *
 * When you create the ringbuffer, you can optionally provide an element
 * destroy callback that frees any allocations inside the element.  This is then
 * automatically called for elements with no tail behind them, ie, elements
 * which don't have any pending consumer are auto-freed.
 *
 * Whole elements may be inserted into the ringbuffer and removed from it, using
 *
 *  - lws_ring_insert()
 *  - lws_ring_consume()
 *
 * You can find out how many whole elements are free or waiting using
 *
 *  - lws_ring_get_count_free_elements()
 *  - lws_ring_get_count_waiting_elements()
 *
 * In addition there are special purpose optional byte-centric apis
 *
 *  - lws_ring_next_linear_insert_range()
 *  - lws_ring_bump_head()
 *
 *  which let you, eg, read() directly into the ringbuffer without needing
 *  an intermediate bounce buffer.
 *
 *  The accessors understand that the ring wraps, and optimizes insertion and
 *  consumption into one or two memcpy()s depending on if the head or tail
 *  wraps.
 *
 *  lws_ring only supports a single head, but optionally multiple tails with
 *  an API to inform it when the "oldest" tail has moved on.  You can give
 *  NULL where-ever an api asks for a tail pointer, and it will use an internal
 *  single tail pointer for convenience.
 *
 *  The "oldest tail", which is the only tail if you give it NULL instead of
 *  some other tail, is used to track which elements in the ringbuffer are
 *  still unread by anyone.
 *
 *   - lws_ring_update_oldest_tail()
 */
///@{
struct lws_ring;

/**
 * lws_ring_create(): create a new ringbuffer
 *
 * \param element_len: the size in bytes of one element in the ringbuffer
 * \param count: the number of elements the ringbuffer can contain
 * \param destroy_element: NULL, or callback to be called for each element
 *			   that is removed from the ringbuffer due to the
 *			   oldest tail moving beyond it
 *
 * Creates the ringbuffer and allocates the storage.  Returns the new
 * lws_ring *, or NULL if the allocation failed.
 *
 * If non-NULL, destroy_element will get called back for every element that is
 * retired from the ringbuffer after the oldest tail has gone past it, and for
 * any element still left in the ringbuffer when it is destroyed.  It replaces
 * all other element destruction code in your user code.
 */
LWS_VISIBLE LWS_EXTERN struct lws_ring *
lws_ring_create(size_t element_len, size_t count,
		void (*destroy_element)(void *element));

/**
 * lws_ring_destroy():  destroy a previously created ringbuffer
 *
 * \param ring: the struct lws_ring to destroy
 *
 * Destroys the ringbuffer allocation and the struct lws_ring itself.
 */
LWS_VISIBLE LWS_EXTERN void
lws_ring_destroy(struct lws_ring *ring);

/**
 * lws_ring_get_count_free_elements():  return how many elements can fit
 *				      in the free space
 *
 * \param ring: the struct lws_ring to report on
 *
 * Returns how much room is left in the ringbuffer for whole element insertion.
 */
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_get_count_free_elements(struct lws_ring *ring);

/**
 * lws_ring_get_count_waiting_elements():  return how many elements can be consumed
 *
 * \param ring: the struct lws_ring to report on
 * \param tail: a pointer to the tail struct to use, or NULL for single tail
 *
 * Returns how many elements are waiting to be consumed from the perspective
 * of the tail pointer given.
 */
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail);

/**
 * lws_ring_insert():  attempt to insert up to max_count elements from src
 *
 * \param ring: the struct lws_ring to report on
 * \param src: the array of elements to be inserted
 * \param max_count: the number of available elements at src
 *
 * Attempts to insert as many of the elements at src as possible, up to the
 * maximum max_count.  Returns the number of elements actually inserted.
 */
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count);

/**
 * lws_ring_consume():  attempt to copy out and remove up to max_count elements
 *		        to src
 *
 * \param ring: the struct lws_ring to report on
 * \param tail: a pointer to the tail struct to use, or NULL for single tail
 * \param dest: the array of elements to be inserted. or NULL for no copy
 * \param max_count: the number of available elements at src
 *
 * Attempts to copy out as many waiting elements as possible into dest, from
 * the perspective of the given tail, up to max_count.  If dest is NULL, the
 * copying out is not done but the elements are logically consumed as usual.
 * NULL dest is useful in combination with lws_ring_get_element(), where you
 * can use the element direct from the ringbuffer and then call this with NULL
 * dest to logically consume it.
 *
 * Increments the tail position according to how many elements could be
 * consumed.
 *
 * Returns the number of elements consumed.
 */
LWS_VISIBLE LWS_EXTERN size_t
lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest,
		 size_t max_count);

/**
 * lws_ring_get_element():  get a pointer to the next waiting element for tail
 *
 * \param ring: the struct lws_ring to report on
 * \param tail: a pointer to the tail struct to use, or NULL for single tail
 *
 * Points to the next element that tail would consume, directly in the
 * ringbuffer.  This lets you write() or otherwise use the element without
 * having to copy it out somewhere first.
 *
 * After calling this, you must call lws_ring_consume(ring, &tail, NULL, 1)
 * which will logically consume the element you used up and increment your
 * tail (tail may also be NULL there if you use a single tail).
 *
 * Returns NULL if no waiting element, or a const void * pointing to it.
 */
LWS_VISIBLE LWS_EXTERN const void *
lws_ring_get_element(struct lws_ring *ring, uint32_t *tail);

/**
 * lws_ring_update_oldest_tail():  free up elements older than tail for reuse
 *
 * \param ring: the struct lws_ring to report on
 * \param tail: a pointer to the tail struct to use, or NULL for single tail
 *
 * If you are using multiple tails, you must use this API to inform the
 * lws_ring when none of the tails still need elements in the fifo any more,
 * by updating it when the "oldest" tail has moved on.
 */
LWS_VISIBLE LWS_EXTERN void
lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail);

/**
 * lws_ring_get_oldest_tail():  get current oldest available data index
 *
 * \param ring: the struct lws_ring to report on
 *
 * If you are initializing a new ringbuffer consumer, you can set its tail to
 * this to start it from the oldest ringbuffer entry still available.
 */
LWS_VISIBLE LWS_EXTERN uint32_t
lws_ring_get_oldest_tail(struct lws_ring *ring);

/**
 * lws_ring_next_linear_insert_range():  used to write directly into the ring
 *
 * \param ring: the struct lws_ring to report on
 * \param start: pointer to a void * set to the start of the next ringbuffer area
 * \param bytes: pointer to a size_t set to the max length you may use from *start
 *
 * This provides a low-level, bytewise access directly into the ringbuffer
 * allowing direct insertion of data without having to use a bounce buffer.
 *
 * The api reports the position and length of the next linear range that can
 * be written in the ringbuffer, ie, up to the point it would wrap, and sets
 * *start and *bytes accordingly.  You can then, eg, directly read() into
 * *start for up to *bytes, and use lws_ring_bump_head() to update the lws_ring
 * with what you have done.
 *
 * Returns nonzero if no insertion is currently possible.
 */
LWS_VISIBLE LWS_EXTERN int
lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
				  size_t *bytes);

/**
 * lws_ring_bump_head():  used to write directly into the ring
 *
 * \param ring: the struct lws_ring to operate on
 * \param bytes: the number of bytes you inserted at the current head
 */
LWS_VISIBLE LWS_EXTERN void
lws_ring_bump_head(struct lws_ring *ring, size_t bytes);

LWS_VISIBLE LWS_EXTERN void
lws_ring_dump(struct lws_ring *ring, uint32_t *tail);

/*
 * This is a helper that combines the common pattern of needing to consume
 * some ringbuffer elements, move the consumer tail on, and check if that
 * has moved any ringbuffer elements out of scope, because it was the last
 * consumer that had not already consumed them.
 *
 * Elements that go out of scope because the oldest tail is now after them
 * get garbage-collected by calling the destroy_element callback on them
 * defined when the ringbuffer was created.
 */

#define lws_ring_consume_and_update_oldest_tail(\
		___ring,    /* the lws_ring object */ \
		___type,    /* type of objects with tails */ \
		___ptail,   /* ptr to tail of obj with tail doing consuming */ \
		___count,   /* count of payload objects being consumed */ \
		___list_head,	/* head of list of objects with tails */ \
		___mtail,   /* member name of tail in ___type */ \
		___mlist  /* member name of next list member ptr in ___type */ \
	) { \
		int ___n, ___m; \
	\
	___n = lws_ring_get_oldest_tail(___ring) == *(___ptail); \
	lws_ring_consume(___ring, ___ptail, NULL, ___count); \
	if (___n) { \
		uint32_t ___oldest; \
		___n = 0; \
		___oldest = *(___ptail); \
		lws_start_foreach_llp(___type **, ___ppss, ___list_head) { \
			___m = (int)lws_ring_get_count_waiting_elements( \
					___ring, &(*___ppss)->___mtail); \
			if (___m >= ___n) { \
				___n = ___m; \
				___oldest = (*___ppss)->___mtail; \
			} \
		} lws_end_foreach_llp(___ppss, ___mlist); \
	\
		lws_ring_update_oldest_tail(___ring, ___oldest); \
	} \
}

/*
 * This does the same as the lws_ring_consume_and_update_oldest_tail()
 * helper, but for the simpler case there is only one consumer, so one
 * tail, and that tail is always the oldest tail.
 */

#define lws_ring_consume_single_tail(\
		___ring,  /* the lws_ring object */ \
		___ptail, /* ptr to tail of obj with tail doing consuming */ \
		___count  /* count of payload objects being consumed */ \
	) { \
	lws_ring_consume(___ring, ___ptail, NULL, ___count); \
	lws_ring_update_oldest_tail(___ring, *(___ptail)); \
}
///@}