Code

22e31a8b4eb0303e063ca6b3bfae2e0e8dcb19ad
[sysdb.git] / src / utils / channel.c
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)
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)
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)
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)
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)
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 : */