Libtrap: Internal development docs  0.11.7
trap_buffer.c
Go to the documentation of this file.
1 /**
2  * \file trap_buffer.c
3  * \brief TRAP ring buffer data structure
4  * \author Tomas Cejka <cejkat@cesnet.cz>
5  * \date 2016
6  */
7 /*
8  * Copyright (C) 2016 CESNET
9  *
10  * LICENSE TERMS
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  * notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  * notice, this list of conditions and the following disclaimer in
19  * the documentation and/or other materials provided with the
20  * distribution.
21  * 3. Neither the name of the Company nor the names of its contributors
22  * may be used to endorse or promote products derived from this
23  * software without specific prior written permission.
24  *
25  * ALTERNATIVELY, provided that this notice is retained in full, this
26  * product may be distributed under the terms of the GNU General Public
27  * License (GPL) version 2 or later, in which case the provisions
28  * of the GPL apply INSTEAD OF those given above.
29  *
30  * This software is provided ``as is'', and any express or implied
31  * warranties, including, but not limited to, the implied warranties of
32  * merchantability and fitness for a particular purpose are disclaimed.
33  * In no event shall the company or contributors be liable for any
34  * direct, indirect, incidental, special, exemplary, or consequential
35  * damages (including, but not limited to, procurement of substitute
36  * goods or services; loss of use, data, or profits; or business
37  * interruption) however caused and on any theory of liability, whether
38  * in contract, strict liability, or tort (including negligence or
39  * otherwise) arising in any way out of the use of this software, even
40  * if advised of the possibility of such damage.
41  *
42  */
43 
44 #include "trap_buffer.h"
45 
46 #ifdef UNIT_TESTING
47 #include <stdarg.h>
48 #include <stddef.h>
49 #include <setjmp.h>
50 #include <cmocka.h>
51 #endif
52 #include <stdint.h>
53 #include <inttypes.h>
54 
55 /**
56  * Change to 1 to enable debug printing.
57  */
58 #define DEBUG_PRINTS 0
59 
60 #if DEBUG_PRINTS
61 #include <stdio.h>
62 #define DBG_PRINT(...) printf(__VA_ARGS__)
63 #else
64 #define DBG_PRINT(...)
65 #endif
66 
67 #define BLOCKSIZE_TOTAL(tb) (tb->blocksize + sizeof(tb_block_t))
68 
69 static inline void _tb_block_clear(tb_block_t *bl)
70 {
71  bl->write_data = (char *) bl->data->data;
72  bl->read_data = bl->write_data;
73  bl->data->size = 0;
74 }
75 
76 static int _tb_block_init(tb_block_t *b)
77 {
78  _tb_block_clear(b);
79  b->refcount = 0;
80  int res = pthread_mutex_init(&b->lock, NULL);
81  if (res == 0) {
82  return TB_SUCCESS;
83  } else {
84  return TB_ERROR;
85  }
86 }
87 
89 {
90  if (pthread_mutex_destroy(&b->lock) == 0) {
91  return TB_SUCCESS;
92  } else {
93  return TB_ERROR;
94  }
95 }
96 
97 static inline tb_block_t *tb_computenext_blockp(trap_buffer_t *tb, uint16_t curidx)
98 {
99  uint16_t bi = (curidx + 1) % tb->nblocks;
100  return (tb_block_t *) (tb->mem + (BLOCKSIZE_TOTAL(tb) * bi));
101 }
102 
104 {
105  uint16_t bi = (tb->cur_wr_block_idx + 1) % tb->nblocks;
106  tb_block_t *bp = tb->blocks[bi];
107  DBG_PRINT("move block from %p to idx %" PRIu32 " %p\n", tb->cur_wr_block, bi, bp);
108 
109  tb->cur_wr_block_idx = bi;
110  tb->cur_wr_block = bp;
111 }
112 
114 {
115  uint16_t bi = (tb->cur_rd_block_idx + 1) % tb->nblocks;
116  tb_block_t *bp = tb->blocks[bi];
117  DBG_PRINT("move block from %p to idx %" PRIu32 " %p\n", tb->cur_rd_block, bi, bp);
118 
119  tb->cur_rd_block_idx = bi;
120  tb->cur_rd_block = bp;
121 }
122 
124 {
125  b->cur_rd_block = (tb_block_t *) b->mem;
126  b->cur_rd_block_idx = 0;
127  DBG_PRINT("move block %p\n", b->cur_rd_block);
128 }
129 
131 {
132  b->cur_wr_block = (tb_block_t *) b->mem;
133  b->cur_wr_block_idx = 0;
134  DBG_PRINT("move block %p\n", b->cur_wr_block);
135 }
136 
138 {
139  if (b->data->size == 0) {
140  return TB_SUCCESS;
141  } else {
142  return TB_FULL;
143  }
144 }
145 
146 trap_buffer_t *tb_init(uint16_t nblocks, uint32_t blocksize)
147 {
148  uint16_t i = 0;
149  trap_buffer_t *n = NULL;
150  if (nblocks == 0 || blocksize == 0) {
151  return NULL;
152  }
153  n = malloc(sizeof(trap_buffer_t));
154  if (n == NULL) {
155  return NULL;
156  }
157 
158  n->blocksize = blocksize;
159  n->nblocks = nblocks;
160  n->mem = malloc(nblocks * BLOCKSIZE_TOTAL(n));
161  if (n->mem == NULL) {
162  free(n);
163  return NULL;
164  }
165 
166  n->blocks = malloc(nblocks * sizeof(tb_block_t *));
167  if (n->blocks == NULL) {
168  free(n->mem);
169  free(n);
170  return NULL;
171  }
172 
173  if (pthread_mutex_init(&n->lock, NULL) != 0) {
174  free(n->mem);
175  free(n);
176  return NULL;
177  }
180 
181  tb_block_t *bl = n->cur_wr_block;
182  for (i = 0; i < nblocks; i++) {
183  _tb_block_init(bl);
184  n->blocks[i] = bl;
185  bl = tb_computenext_blockp(n, i);
186  }
187 
188  return n;
189 }
190 
192 {
193  int i;
194  if (b == NULL || (*b) == NULL) {
195  return;
196  }
197 
198  trap_buffer_t *p = (*b);
199 
201  for (i = 0; i < p->nblocks; i++) {
203  tb_next_wr_block(p);
204  }
205 
206  free(p->mem);
207  free(p->blocks);
208  free(p);
209  (*b) = NULL;
210 }
211 
213 {
214  return pthread_mutex_lock(&b->lock);
215 }
216 
218 {
219  return pthread_mutex_unlock(&b->lock);
220 }
221 
223 {
224  return pthread_mutex_lock(&bl->lock);
225 }
226 
228 {
229  return pthread_mutex_unlock(&bl->lock);
230 }
231 
232 static inline int _tb_pushmess_checksize(trap_buffer_t *b, tb_block_t **bl, uint32_t ts)
233 {
234  uint32_t maxsize = (b->blocksize - sizeof(struct tb_block_data_s));
235  tb_block_t *blp = *bl;
236 
237  if (((*bl)->data->size + ts) > maxsize) {
238  DBG_PRINT("No memory for %" PRIu16 " msg, total size: %" PRIu32 ", hdr: %" PRIu32 "\n",
239  size, ((*bl)->data->size + ts), (*bl)->data->size);
240  if (ts > maxsize) {
241  DBG_PRINT("Message is bigger than block.\n");
242  return TB_ERROR;
243  }
244 
245  /* move to the next block in locked time */
246  tb_block_unlock(blp);
247  tb_next_wr_block(b);
248  blp = b->cur_wr_block;
249  tb_block_lock(blp);
250  (*bl) = blp;
251 
252  if (tb_isblockfree(*bl) == TB_FULL) {
253  return TB_FULL;
254  } else {
255  DBG_PRINT("Moved to the next block.\n");
256  return TB_USED_NEWBLOCK;
257  }
258  }
259  return TB_SUCCESS;
260 }
261 
262 int tb_pushmess(trap_buffer_t *b, const void *data, uint16_t size)
263 {
264  int res = TB_SUCCESS;
265 
266  tb_lock(b);
267  tb_block_t *bl = b->cur_wr_block;
268  tb_block_lock(bl);
269  uint32_t ts = size + sizeof(size);
270 
271  res = _tb_pushmess_checksize(b, &bl, ts);
272  tb_unlock(b);
273 
274  if (res == TB_ERROR || res == TB_FULL) {
275  goto exit;
276  }
277 
278  uint16_t *msize = (uint16_t *) bl->write_data;
279  char *p = (char *) (msize + 1);
280  (*msize) = htons(size);
281  memcpy(p, data, size);
282  bl->data->size += size + sizeof(size);
283  bl->write_data += size + sizeof(size);
284  DBG_PRINT("Saved %" PRIu16 " B, total: %" PRIu32 " B\n", size, (bl->data->size + ts));
285 
286 exit:
287  tb_block_unlock(bl);
288  return res;
289 }
290 
291 int tb_pushmess2(trap_buffer_t *b, const void *d1, uint16_t s1, const void *d2, uint16_t s2)
292 {
293  int res = TB_SUCCESS;
294  uint32_t ts = s1 + s2 + sizeof(ts);
295 
296  tb_lock(b);
297  tb_block_t *bl = b->cur_wr_block;
298  tb_block_lock(bl);
299 
300  res = _tb_pushmess_checksize(b, &bl, ts);
301  tb_unlock(b);
302 
303  if (res == TB_ERROR || res == TB_FULL) {
304  goto exit;
305  }
306 
307  uint16_t *msize = (uint16_t *) bl->write_data;
308  char *p = (char *) (msize + 1);
309 
310  (*msize) = htons(ts);
311  memcpy(p, d1, s1);
312  p += s1;
313  memcpy(p, d2, s2);
314  bl->data->size += ts + sizeof(ts);
315  bl->write_data += ts + sizeof(ts);
316  DBG_PRINT("Saved 2-part-msg %" PRIu16 " B, total: %" PRIu32 " B\n", ts, (bl->data->size + ts));
317 
318 exit:
319  tb_block_unlock(bl);
320  return res;
321 }
322 
323 int tb_getmess(trap_buffer_t *b, const void **data, uint16_t *size)
324 {
325  int res = TB_SUCCESS;
326  tb_lock(b);
327  tb_block_t *bl = b->cur_rd_block;
328  tb_block_lock(bl);
329 
330  if (bl->read_data == bl->write_data) {
331  DBG_PRINT("Not enough memory in %" PRIu32 " block for %" PRIu16 " message, total size: %" PRIu32 ", header: %" PRIu32 "\n",
332  b->blocksize, size, (bl->data->size + size + sizeof(size)), bl->data->size);
333  tb_block_unlock(bl);
334  tb_next_rd_block(b);
335  bl = b->cur_rd_block;
336  tb_block_lock(bl);
337  if (bl->read_data >= bl->write_data) {
338  tb_block_unlock(bl);
339  tb_unlock(b);
340  return TB_EMPTY;
341  } else {
342  res = TB_USED_NEWBLOCK;
343  DBG_PRINT("Moved to the next block.\n");
344  }
345  }
346  tb_unlock(b);
347 
348  uint16_t *msize = (uint16_t *) bl->read_data;
349  (*data) = (const void *) (msize + 1);
350  (*size) = ntohs(*msize);
351  bl->read_data += sizeof(uint16_t) + (*size);
352 
353  tb_block_unlock(bl);
354  return res;
355 }
356 
358 {
359  uint16_t i;
360  tb_block_t *bl;
361  tb_lock(tb);
362  for (i = 0; i < tb->nblocks; i++) {
363  bl = tb->blocks[i];
364  tb_block_lock(bl);
365  if (bl->refcount == 0) {
366  _tb_block_clear(bl);
367  }
368  tb_block_unlock(bl);
369  }
370  tb_unlock(tb);
371 }
372 
#define TB_EMPTY
Definition: trap_buffer.h:57
uint16_t cur_wr_block_idx
Definition: trap_buffer.h:118
int tb_unlock(trap_buffer_t *b)
Definition: trap_buffer.c:217
void tb_first_rd_block(trap_buffer_t *b)
Definition: trap_buffer.c:123
#define BLOCKSIZE_TOTAL(tb)
Definition: trap_buffer.c:67
static tb_block_t * tb_computenext_blockp(trap_buffer_t *tb, uint16_t curidx)
Definition: trap_buffer.c:97
int tb_pushmess2(trap_buffer_t *b, const void *d1, uint16_t s1, const void *d2, uint16_t s2)
Definition: trap_buffer.c:291
pthread_mutex_t lock
Definition: trap_buffer.h:92
#define TB_SUCCESS
Definition: trap_buffer.h:53
#define TB_ERROR
Definition: trap_buffer.h:54
uint16_t cur_rd_block_idx
Definition: trap_buffer.h:128
void tb_next_rd_block(trap_buffer_t *tb)
Definition: trap_buffer.c:113
char * read_data
Definition: trap_buffer.h:82
uint32_t size
Definition: trap_buffer.h:66
int tb_block_unlock(tb_block_t *bl)
Definition: trap_buffer.c:227
static void _tb_block_clear(tb_block_t *bl)
Definition: trap_buffer.c:69
int tb_lock(trap_buffer_t *b)
Definition: trap_buffer.c:212
uint16_t refcount
Definition: trap_buffer.h:87
uint8_t data[0]
static int _tb_block_destroy(tb_block_t *b)
Definition: trap_buffer.c:88
void tb_first_wr_block(trap_buffer_t *b)
Definition: trap_buffer.c:130
static int _tb_pushmess_checksize(trap_buffer_t *b, tb_block_t **bl, uint32_t ts)
Definition: trap_buffer.c:232
int tb_pushmess(trap_buffer_t *b, const void *data, uint16_t size)
Definition: trap_buffer.c:262
void tb_destroy(trap_buffer_t **b)
Definition: trap_buffer.c:191
int tb_block_lock(tb_block_t *bl)
Definition: trap_buffer.c:222
tb_block_t ** blocks
Definition: trap_buffer.h:114
#define TB_USED_NEWBLOCK
Definition: trap_buffer.h:56
static int _tb_block_init(tb_block_t *b)
Definition: trap_buffer.c:76
void tb_next_wr_block(trap_buffer_t *tb)
Definition: trap_buffer.c:103
void tb_clear_unused(trap_buffer_t *tb)
Definition: trap_buffer.c:357
trap_buffer_t * tb_init(uint16_t nblocks, uint32_t blocksize)
Definition: trap_buffer.c:146
#define DBG_PRINT(...)
Definition: trap_buffer.c:64
uint32_t blocksize
Definition: trap_buffer.h:133
tb_block_t * cur_wr_block
Definition: trap_buffer.h:109
char * write_data
Definition: trap_buffer.h:77
uint16_t nblocks
Definition: trap_buffer.h:138
#define TB_FULL
Definition: trap_buffer.h:55
pthread_mutex_t lock
Definition: trap_buffer.h:143
int tb_getmess(trap_buffer_t *b, const void **data, uint16_t *size)
Definition: trap_buffer.c:323
int tb_isblockfree(tb_block_t *b)
Definition: trap_buffer.c:137
tb_block_t * cur_rd_block
Definition: trap_buffer.h:123
struct tb_block_data_s data[0]
Definition: trap_buffer.h:97
TRAP ring buffer data structure.