1 /*-
2 * SSLsplit - transparent SSL/TLS interception
3 * https://www.roe.ch/SSLsplit
4 *
5 * Copyright (c) 2009-2019, Daniel Roethlisberger <daniel@roe.ch>.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "thrqueue.h"
30
31 #include <stdlib.h>
32 #include <string.h>
33 #include <pthread.h>
34
35 /*
36 * Thread-safe, bounded-size queue based on pthreads mutex and conds.
37 * Both enqueue and dequeue are available in a blocking and non-blocking
38 * version.
39 */
40
41 struct thrqueue {
42 void **data;
43 size_t sz, n;
44 size_t in, out;
45 unsigned int block_enqueue : 1;
46 unsigned int block_dequeue : 1;
47 pthread_mutex_t mutex;
48 pthread_cond_t notempty;
49 pthread_cond_t notfull;
50 };
51
52 /*
53 * Create a new thread-safe queue of size sz.
54 */
55 thrqueue_t *
thrqueue_new(size_t sz)56 thrqueue_new(size_t sz)
57 {
58 thrqueue_t *queue;
59
60 if (!(queue = malloc(sizeof(thrqueue_t))))
61 goto out0;
62 if (!(queue->data = malloc(sz * sizeof(void*))))
63 goto out1;
64 if (pthread_mutex_init(&queue->mutex, NULL))
65 goto out2;
66 if (pthread_cond_init(&queue->notempty, NULL))
67 goto out3;
68 if (pthread_cond_init(&queue->notfull, NULL))
69 goto out4;
70 queue->sz = sz;
71 queue->n = 0;
72 queue->in = 0;
73 queue->out = 0;
74 queue->block_enqueue = 1;
75 queue->block_dequeue = 1;
76 return queue;
77
78 out4:
79 pthread_cond_destroy(&queue->notempty);
80 out3:
81 pthread_mutex_destroy(&queue->mutex);
82 out2:
83 free(queue->data);
84 out1:
85 free(queue);
86 out0:
87 return NULL;
88 }
89
90 /*
91 * Free all resources associated with queue.
92 * The caller must ensure that there are no threads still
93 * using the queue when it is free'd.
94 */
95 void
thrqueue_free(thrqueue_t * queue)96 thrqueue_free(thrqueue_t *queue)
97 {
98 free(queue->data);
99 pthread_mutex_destroy(&queue->mutex);
100 pthread_cond_destroy(&queue->notempty);
101 pthread_cond_destroy(&queue->notfull);
102 free(queue);
103 }
104
105 /*
106 * Enqueue an item into the queue. Will block if the queue is full.
107 * If enqueue has been switched to non-blocking mode, never blocks
108 * but instead returns NULL if queue is full.
109 * Returns enqueued item on success.
110 */
111 void *
thrqueue_enqueue(thrqueue_t * queue,void * item)112 thrqueue_enqueue(thrqueue_t *queue, void *item)
113 {
114 pthread_mutex_lock(&queue->mutex);
115 while (queue->n == queue->sz) {
116 if (!queue->block_enqueue) {
117 pthread_mutex_unlock(&queue->mutex);
118 return NULL;
119 }
120 pthread_cond_wait(&queue->notfull, &queue->mutex);
121 }
122 queue->data[queue->in++] = item;
123 queue->in %= queue->sz;
124 queue->n++;
125 pthread_mutex_unlock(&queue->mutex);
126 pthread_cond_broadcast(&queue->notempty);
127 return item;
128 }
129
130 /*
131 * Non-blocking enqueue. Never blocks.
132 * Returns NULL if the queue is full.
133 * Returns the enqueued item on success.
134 */
135 void *
thrqueue_enqueue_nb(thrqueue_t * queue,void * item)136 thrqueue_enqueue_nb(thrqueue_t *queue, void *item)
137 {
138 pthread_mutex_lock(&queue->mutex);
139 if (queue->n == queue->sz) {
140 pthread_mutex_unlock(&queue->mutex);
141 return NULL;
142 }
143 queue->data[queue->in++] = item;
144 queue->in %= queue->sz;
145 queue->n++;
146 pthread_mutex_unlock(&queue->mutex);
147 pthread_cond_signal(&queue->notempty);
148 return item;
149 }
150
151 /*
152 * Dequeue an item from the queue. Will block if the queue is empty.
153 * If dequeue has been switched to non-blocking mode, never blocks
154 * but instead returns NULL if queue is empty.
155 * Returns dequeued item on success.
156 */
157 void *
thrqueue_dequeue(thrqueue_t * queue)158 thrqueue_dequeue(thrqueue_t *queue)
159 {
160 void *item;
161
162 pthread_mutex_lock(&queue->mutex);
163 while (queue->n == 0) {
164 if (!queue->block_dequeue) {
165 pthread_mutex_unlock(&queue->mutex);
166 return NULL;
167 }
168 pthread_cond_wait(&queue->notempty, &queue->mutex);
169 }
170 item = queue->data[queue->out++];
171 queue->out %= queue->sz;
172 queue->n--;
173 pthread_mutex_unlock(&queue->mutex);
174 pthread_cond_signal(&queue->notfull);
175 return item;
176 }
177
178 /*
179 * Non-blocking dequeue. Never blocks.
180 * Returns NULL if the queue is empty.
181 * Returns the dequeued item on success.
182 */
183 void *
thrqueue_dequeue_nb(thrqueue_t * queue)184 thrqueue_dequeue_nb(thrqueue_t *queue)
185 {
186 void *item;
187
188 pthread_mutex_lock(&queue->mutex);
189 if (queue->n == 0) {
190 pthread_mutex_unlock(&queue->mutex);
191 return NULL;
192 }
193 item = queue->data[queue->out++];
194 queue->out %= queue->sz;
195 queue->n--;
196 pthread_mutex_unlock(&queue->mutex);
197 pthread_cond_signal(&queue->notfull);
198 return item;
199 }
200
201 /*
202 * Permanently make all enqueue operations on queue non-blocking and wake
203 * up all threads currently waiting for the queue to become not full.
204 * This is to allow threads to finish their work on the queue on application
205 * shutdown, but not be blocked forever.
206 */
207 void
thrqueue_unblock_enqueue(thrqueue_t * queue)208 thrqueue_unblock_enqueue(thrqueue_t *queue)
209 {
210 queue->block_enqueue = 0;
211 pthread_cond_broadcast(&queue->notfull);
212 sched_yield();
213 }
214
215 /*
216 * Permanently make all dequeue operations on queue non-blocking and wake
217 * up all threads currently waiting for the queue to become not empty.
218 * This is to allow threads to finish their work on the queue on application
219 * shutdown, but not be blocked forever.
220 */
221 void
thrqueue_unblock_dequeue(thrqueue_t * queue)222 thrqueue_unblock_dequeue(thrqueue_t *queue)
223 {
224 queue->block_dequeue = 0;
225 pthread_cond_broadcast(&queue->notempty);
226 sched_yield();
227 }
228
229 /* vim: set noet ft=c: */
230