add library versioning
[nobug.git] / src / nobug_ringbuffer.c
blobb051fc779da7c56a42d5a5a592e922ab4dd5ee82
1 /*
2 This file is part of the NoBug debugging library.
4 Copyright (C)
5 2007, 2008, 2009, 2010, Christian Thaeter <ct@pipapo.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, contact Christian Thaeter <ct@pipapo.org>.
20 #include <fcntl.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <stdlib.h>
25 #include <sys/mman.h>
27 #define NOBUG_LIBNOBUG_C
28 #include "nobug.h"
30 #include "llist.h"
32 #if NOBUG_USE_PTHREAD
33 pthread_mutex_t nobug_ringbuffer_mutex = PTHREAD_MUTEX_INITIALIZER;
34 #endif
35 LLIST_AUTO(nobug_ringbuffer_registry);
38 //ringbuffer Ringbuffer
39 //ringbuffer ----------
40 //ringbuffer
41 //ringbuffer Implementation Details
42 //ringbuffer ~~~~~~~~~~~~~~~~~~~~~~
43 //ringbuffer
44 //ringbuffer This Ringbuffer implementation uses mmaped guard pages to implement fast wraparond
45 //ringbuffer by utilizing the MMU hardware. The guard pages mirror the beginning and end of the
46 //ringbuffer ringbuffers memory after respecive before it. By that one can write over the end
47 //ringbuffer without checking for wraparounds in one go, up to the size of the guard areas.
48 //ringbuffer
49 //ringbuffer guard before | ringbuffer memory | guard after
50 //ringbuffer aaaa|bbbbb aaaaa|bbbbb
51 //ringbuffer
52 //ringbuffer In the simpliest/smallest case this means that one page of memory is just mirrored
53 //ringbuffer 3 times after each other.
54 //ringbuffer
55 //ringbuffer
56 //ringbuffer In Memory Format
57 //ringbuffer ~~~~~~~~~~~~~~~~
58 //ringbuffer
59 //ringbuffer This implementation is meant for string data only, each stored string is delimited
60 //ringbuffer by a '\0' from the next one. There is only a write position, not a read position like
61 //ringbuffer in a queue. The write position is indicated by '\0xff' as sentinel value (for
62 //ringbuffer implementing persistent writebuffers). Even with only a write position, it is possible
63 //ringbuffer to iterate over existing entries, pop the last written entry, append to it and so on.
64 //ringbuffer But all this operations must not interleave with writes which may run over and invalidate
65 //ringbuffer any data queried from it.
66 //ringbuffer
70 struct nobug_ringbuffer*
71 nobug_ringbuffer_init (struct nobug_ringbuffer* self, size_t size, size_t guard, const char * name, int flags)
73 size_t pagesz = sysconf (_SC_PAGESIZE);
75 /* align size and guard on page boundaries, minimum is 1 page */
76 size = self->size = (size?size:pagesz + pagesz-1) & ~(pagesz-1);
77 guard = self->guard = (guard?guard:pagesz + pagesz-1) & ~(pagesz-1);
79 /* there is no point in having guards biggier than size */
80 if (guard > size)
81 guard = size;
83 self->name[255] = '\0';
84 if (!name)
86 strcpy(self->name, "/tmp/nobug_ringbufferXXXXXX");
88 else
90 strncpy(self->name, name, 255);
93 int fd = mkstemp(self->name);
95 int oflags = O_RDWR|O_CREAT;
96 if (!(flags & NOBUG_RINGBUFFER_APPEND))
97 oflags |= O_TRUNC;
99 if (fd == -1 && errno == EINVAL)
100 fd = open(self->name, oflags, 0600);
102 if (fd == -1)
104 /* still error, exit here */
105 fprintf(stderr, "nobug_ringbuffer: Failed to open nobug_ringbuffer %s: %s\n", self->name, strerror(errno));
106 exit (EXIT_FAILURE);
109 if (!name || flags & NOBUG_RINGBUFFER_TEMP)
110 unlink (self->name);
112 if (!name || !(flags & NOBUG_RINGBUFFER_KEEP))
114 /* just in case we need the name later, store the first character at the end */
115 self->name[255] = self->name[0];
116 self->name[0] = 0;
119 if (ftruncate(fd, size))
121 fprintf(stderr, "nobug_ringbuffer: Failed to truncate ringbuffer to %zd: %s\n",
122 size, strerror(errno));
123 exit (EXIT_FAILURE);
126 /* get contigous address range */
127 char * start = mmap (0, size+2*self->guard, PROT_NONE, MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0);
128 if (!start)
130 fprintf(stderr, "nobug_ringbuffer: Failed to reserve %zd bytes of address space: %s\n",
131 size+2*self->guard, strerror(errno));
132 exit (EXIT_FAILURE);
135 /* map the backing file */
136 self->start = mmap (start+self->guard, size, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, 0);
137 if (!self->start)
139 fprintf(stderr, "nobug_ringbuffer: Failed to mmap backing file, %s\n", strerror(errno));
140 exit (EXIT_FAILURE);
143 /* map beginning after the end */
144 if (!mmap (start+self->guard+size, self->guard, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, 0))
146 fprintf(stderr, "nobug_ringbuffer: Failed to mmap trailing guard page, %s\n", strerror(errno));
147 exit (EXIT_FAILURE);
150 /* map end before beginning */
151 if (!mmap (start, self->guard, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, size-self->guard))
153 fprintf(stderr, "nobug_ringbuffer: Failed to mmap leading guard page, %s\n", strerror(errno));
154 exit (EXIT_FAILURE);
157 close (fd);
159 if (flags & NOBUG_RINGBUFFER_APPEND)
161 start = memchr (self->start, ~0, size);
162 if (!start)
163 /* new file, can't append */
164 goto init;
165 self->pos = start-1;
166 if (self->pos < self->start)
167 self->pos += size;
169 else
171 init:
172 /* set pos to the end of the file, then new writes will start at the beginning */
173 self->pos = self->start+size-1;
174 /* ~0 is used as marker for the turnaround point */
175 self->pos[1] = ~0;
178 #if NOBUG_USE_PTHREAD
179 pthread_mutex_lock (&nobug_ringbuffer_mutex);
180 llist_insert_tail (&nobug_ringbuffer_registry, llist_init (&self->node));
181 pthread_mutex_unlock (&nobug_ringbuffer_mutex);
182 #else
183 llist_insert_tail (&nobug_ringbuffer_registry, llist_init (&self->node));
184 #endif
186 return self;
190 struct nobug_ringbuffer*
191 nobug_ringbuffer_new (size_t size, size_t guard, const char * name, int flags)
193 struct nobug_ringbuffer* self = malloc (sizeof (struct nobug_ringbuffer));
194 if (!self)
195 return NULL;
196 return nobug_ringbuffer_init (self, guard, size, name, flags);
200 struct nobug_ringbuffer*
201 nobug_ringbuffer_destroy (struct nobug_ringbuffer* self)
203 #if NOBUG_USE_PTHREAD
204 pthread_mutex_lock (&nobug_ringbuffer_mutex);
205 llist_unlink (&self->node);
206 pthread_mutex_unlock (&nobug_ringbuffer_mutex);
207 #else
208 llist_unlink (&self->node);
209 #endif
211 if (self->name[0])
212 unlink(self->name);
213 munmap(self->start-self->guard, self->size + 2 * self->guard);
214 return self;
218 void
219 nobug_ringbuffer_delete (struct nobug_ringbuffer* self)
221 free (nobug_ringbuffer_destroy (self));
225 void
226 nobug_ringbuffer_sync (struct nobug_ringbuffer* self)
228 msync (self->start, self->size, MS_SYNC);
232 void
233 nobug_ringbuffer_allsync (void)
235 #if NOBUG_USE_PTHREAD
236 pthread_mutex_lock (&nobug_ringbuffer_mutex);
237 LLIST_FOREACH(&nobug_ringbuffer_registry, n)
238 nobug_ringbuffer_sync ((struct nobug_ringbuffer*) n);
239 pthread_mutex_unlock (&nobug_ringbuffer_mutex);
240 #else
241 LLIST_FOREACH(&nobug_ringbuffer_registry, n)
242 nobug_ringbuffer_sync ((struct nobug_ringbuffer*) n);
243 #endif
248 nobug_ringbuffer_vprintf (struct nobug_ringbuffer* self, const char* fmt, va_list ap)
250 int written = vsnprintf (self->pos + 1, self->guard-2, fmt, ap);
252 self->pos += (written + 1);
254 if (self->pos > self->start+self->size)
255 self->pos -= self->size;
257 self->pos[1] = ~0;
259 return written;
264 nobug_ringbuffer_printf (struct nobug_ringbuffer* self, const char* fmt, ...)
266 va_list ap;
267 va_start (ap, fmt);
268 int written = nobug_ringbuffer_vprintf (self, fmt, ap);
269 va_end(ap);
270 return written;
274 char*
275 nobug_ringbuffer_append (struct nobug_ringbuffer* self)
277 if (self->pos[-1] != 0)
278 --self->pos;
280 return self->pos;
285 resizes the current (last printed) message, the extended space filled with 'fill' characters unless
286 fill == 0, then the extended space is left untouched and expected to be properly set up by the client
287 returns 0 on failure (newsize would overflow the guard area)
290 nobug_ringbuffer_extend (struct nobug_ringbuffer* self, size_t newsize, const char fill)
292 char* prev = nobug_ringbuffer_prev (self, NULL);
294 if (prev >= self->start+self->size)
295 prev -= self->size;
297 if (prev+newsize+2 > self->start + self->size + self->guard)
298 return 0;
300 if (fill)
301 memset(self->pos, fill, prev+newsize-self->pos);
303 self->pos = prev+newsize;
304 self->pos[0] = '\0';
305 self->pos[1] = ~0;
307 return 1;
311 char*
312 nobug_ringbuffer_prev (struct nobug_ringbuffer* self, char* pos)
314 if (!pos)
315 pos = self->pos;
316 else
317 --pos;
319 while(!*--pos);
320 while(*--pos);
321 if (pos < self->start)
322 pos += self->size;
324 if (pos[1] == (char)~0)
325 return NULL;
327 return pos+1;
331 char*
332 nobug_ringbuffer_next (struct nobug_ringbuffer* self, char* pos)
334 if (!pos)
335 pos = self->pos+1;
337 while (*++pos);
338 while (!*++pos);
339 if (pos > self->start+self->size)
340 pos -= self->size;
342 if (*pos == (char)~0)
343 return NULL;
345 return pos;
350 nobug_ringbuffer_save (struct nobug_ringbuffer* self, FILE* out)
352 int ret = 0;
353 int cnt;
354 char* next;
356 for (next = nobug_ringbuffer_next (self, NULL); next; next = nobug_ringbuffer_next(self, next))
358 cnt = fprintf (out,"%s\n", next);
359 if (cnt < 0)
360 return -ret-1;
361 else
362 ret += cnt;
364 return ret;
368 nobug_ringbuffer_load (struct nobug_ringbuffer* self, FILE* in)
370 int ret = 0;
371 char buf[self->guard];
373 while (fgets(buf, self->guard, in))
375 size_t l = strlen(buf);
376 if (buf[l-1] == '\n')
377 buf[l-1] = '\0';
378 ret += nobug_ringbuffer_printf (self, "%s", buf);
380 return ret;
384 char*
385 nobug_ringbuffer_pos (struct nobug_ringbuffer* self)
387 return self->pos+1;
391 void
392 nobug_ringbuffer_pop (struct nobug_ringbuffer* self)
394 self->pos[0] = '\n'; /* clear the \0 */
395 self->pos[1] = ' '; /* clear the ~0 */
397 self->pos = strrchr(self->pos, 0);
398 if (self->pos < self->start)
399 self->pos += self->size;
401 self->pos[1] = ~0;