1 /*
2 * SysDB - src/utils/channel.c
3 * Copyright (C) 2013 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 #include "utils/channel.h"
30 #include <assert.h>
32 #include <stdlib.h>
33 #include <string.h>
35 #include <pthread.h>
37 /*
38 * private data types
39 */
41 struct sdb_channel {
42 pthread_rwlock_t lock;
44 /* maybe TODO: add support for 'nil' values using a boolean area */
46 void *data;
47 size_t data_len;
48 size_t elem_size;
50 size_t head;
51 size_t tail;
52 _Bool full;
53 };
55 /*
56 * private helper functions
57 */
59 #define NEXT_WRITE(chan) (((chan)->tail + 1) % (chan)->data_len)
60 #define NEXT_READ(chan) (((chan)->head + 1) % (chan)->data_len)
62 #define ELEM(chan, i) \
63 (void *)((char *)(chan)->data + (i) * (chan)->elem_size)
64 #define TAIL(chan) ELEM(chan, (chan)->tail)
65 #define HEAD(chan) ELEM(chan, (chan)->head)
67 /* Insert a new element at the end. */
68 static int
69 channel_write(sdb_channel_t *chan, const void *data)
70 {
71 assert(chan && data);
73 if (chan->full)
74 return -1;
76 memcpy(TAIL(chan), data, chan->elem_size);
77 chan->tail = NEXT_WRITE(chan);
79 chan->full = chan->tail == chan->head;
80 return 0;
81 } /* channel_write */
83 /* retrieve the first element */
84 static int
85 channel_read(sdb_channel_t *chan, void *data)
86 {
87 assert(chan && data);
89 if ((chan->head == chan->tail) && (! chan->full))
90 return -1;
92 memcpy(data, HEAD(chan), chan->elem_size);
93 chan->head = NEXT_READ(chan);
95 chan->full = 0;
96 return 0;
97 } /* channel_read */
99 /*
100 * public API
101 */
103 sdb_channel_t *
104 sdb_channel_create(size_t size, size_t elem_size)
105 {
106 sdb_channel_t *chan;
108 if (! elem_size)
109 return NULL;
110 if (! size)
111 size = 1;
113 chan = calloc(1, sizeof(*chan));
114 if (! chan)
115 return NULL;
117 chan->data = calloc(size, elem_size);
118 if (! chan->data) {
119 sdb_channel_destroy(chan);
120 return NULL;
121 }
123 chan->data_len = size;
124 chan->elem_size = elem_size;
126 pthread_rwlock_init(&chan->lock, /* attr = */ NULL);
128 chan->head = chan->tail = 0;
129 return chan;
130 } /* sdb_channel_create */
132 void
133 sdb_channel_destroy(sdb_channel_t *chan)
134 {
135 if (! chan)
136 return;
138 pthread_rwlock_wrlock(&chan->lock);
139 free(chan->data);
140 chan->data = NULL;
141 chan->data_len = 0;
143 pthread_rwlock_unlock(&chan->lock);
144 pthread_rwlock_destroy(&chan->lock);
145 free(chan);
146 } /* sdb_channel_destroy */
148 int
149 sdb_channel_write(sdb_channel_t *chan, const void *data)
150 {
151 int status;
153 if ((! chan) || (! data))
154 return -1;
156 pthread_rwlock_wrlock(&chan->lock);
157 status = channel_write(chan, data);
158 pthread_rwlock_unlock(&chan->lock);
159 return status;
160 } /* sdb_channel_write */
162 int
163 sdb_channel_read(sdb_channel_t *chan, void *data)
164 {
165 int status;
167 if ((! chan) || (! data))
168 return -1;
170 pthread_rwlock_wrlock(&chan->lock);
171 status = channel_read(chan, data);
172 pthread_rwlock_unlock(&chan->lock);
173 return status;
174 } /* sdb_channel_read */
176 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */