22e31a8b4eb0303e063ca6b3bfae2e0e8dcb19ad
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_mutex_t lock;
44 /* signaling for select() operation */
45 pthread_cond_t cond;
47 /* maybe TODO: add support for 'nil' values using a boolean area */
49 void *data;
50 size_t data_len;
51 size_t elem_size;
53 size_t head;
54 size_t tail;
55 _Bool full;
56 };
58 /*
59 * private helper functions
60 */
62 #define NEXT_WRITE(chan) (((chan)->tail + 1) % (chan)->data_len)
63 #define NEXT_READ(chan) (((chan)->head + 1) % (chan)->data_len)
65 #define ELEM(chan, i) \
66 (void *)((char *)(chan)->data + (i) * (chan)->elem_size)
67 #define TAIL(chan) ELEM(chan, (chan)->tail)
68 #define HEAD(chan) ELEM(chan, (chan)->head)
70 /* Insert a new element at the end; chan->lock must be held.
71 * Returns 0 if data has been written or if data may be written
72 * if 'data' is NULL. */
73 static int
74 channel_write(sdb_channel_t *chan, const void *data)
75 {
76 assert(chan);
78 if (chan->full)
79 return -1;
80 else if (! data)
81 return 0;
83 memcpy(TAIL(chan), data, chan->elem_size);
84 chan->tail = NEXT_WRITE(chan);
86 chan->full = chan->tail == chan->head;
87 pthread_cond_broadcast(&chan->cond);
88 return 0;
89 } /* channel_write */
91 /* Retrieve the first element; chan->lock must be held.
92 * Returns 0 if data has been read or if data is available
93 * if 'data' is NULL. */
94 static int
95 channel_read(sdb_channel_t *chan, void *data)
96 {
97 assert(chan);
99 if ((chan->head == chan->tail) && (! chan->full))
100 return -1;
101 else if (! data)
102 return 0;
104 memcpy(data, HEAD(chan), chan->elem_size);
105 chan->head = NEXT_READ(chan);
107 chan->full = 0;
108 pthread_cond_broadcast(&chan->cond);
109 return 0;
110 } /* channel_read */
112 /*
113 * public API
114 */
116 sdb_channel_t *
117 sdb_channel_create(size_t size, size_t elem_size)
118 {
119 sdb_channel_t *chan;
121 if (! elem_size)
122 return NULL;
123 if (! size)
124 size = 1;
126 chan = calloc(1, sizeof(*chan));
127 if (! chan)
128 return NULL;
130 chan->data = calloc(size, elem_size);
131 if (! chan->data) {
132 sdb_channel_destroy(chan);
133 return NULL;
134 }
136 chan->data_len = size;
137 chan->elem_size = elem_size;
139 pthread_mutex_init(&chan->lock, /* attr = */ NULL);
140 pthread_cond_init(&chan->cond, /* attr = */ NULL);
142 chan->head = chan->tail = 0;
143 return chan;
144 } /* sdb_channel_create */
146 void
147 sdb_channel_destroy(sdb_channel_t *chan)
148 {
149 if (! chan)
150 return;
152 pthread_mutex_lock(&chan->lock);
153 free(chan->data);
154 chan->data = NULL;
155 chan->data_len = 0;
157 pthread_cond_destroy(&chan->cond);
159 pthread_mutex_unlock(&chan->lock);
160 pthread_mutex_destroy(&chan->lock);
161 free(chan);
162 } /* sdb_channel_destroy */
164 int
165 sdb_channel_select(sdb_channel_t *chan, int *wantread, void *read_data,
166 int *wantwrite, void *write_data, const struct timespec *timeout)
167 {
168 int status = 0;
170 if (! chan)
171 return -1;
173 if ((! wantread) && (! read_data) && (! wantwrite) && (! write_data))
174 return -1;
176 pthread_mutex_lock(&chan->lock);
177 while (! status) {
178 int read_status, write_status;
180 read_status = channel_read(chan, read_data);
181 write_status = channel_write(chan, write_data);
183 if ((! read_status) || (! write_status)) {
184 if (wantread)
185 *wantread = read_status == 0;
186 if (wantwrite)
187 *wantwrite = write_status == 0;
189 if (((wantread || read_data) && (! read_status))
190 || ((wantwrite || write_data) && (! write_status)))
191 break;
192 }
194 if (timeout)
195 status = pthread_cond_timedwait(&chan->cond, &chan->lock,
196 timeout);
197 else
198 status = pthread_cond_wait(&chan->cond, &chan->lock);
199 }
201 pthread_mutex_unlock(&chan->lock);
202 return status;
203 } /* sdb_channel_select */
205 int
206 sdb_channel_write(sdb_channel_t *chan, const void *data)
207 {
208 int status;
210 if ((! chan) || (! data))
211 return -1;
213 pthread_mutex_lock(&chan->lock);
214 status = channel_write(chan, data);
215 pthread_mutex_unlock(&chan->lock);
216 return status;
217 } /* sdb_channel_write */
219 int
220 sdb_channel_read(sdb_channel_t *chan, void *data)
221 {
222 int status;
224 if ((! chan) || (! data))
225 return -1;
227 pthread_mutex_lock(&chan->lock);
228 status = channel_read(chan, data);
229 pthread_mutex_unlock(&chan->lock);
230 return status;
231 } /* sdb_channel_read */
233 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */