summaryrefslogtreecommitdiff
path: root/ot_tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_tcp.c')
-rw-r--r--ot_tcp.c83
1 files changed, 82 insertions, 1 deletions
diff --git a/ot_tcp.c b/ot_tcp.c
index 8647a47..47697ca 100644
--- a/ot_tcp.c
+++ b/ot_tcp.c
@@ -5,6 +5,10 @@
5 5
6/* System */ 6/* System */
7#include <errno.h> 7#include <errno.h>
8#include <pthread.h>
9#include <stdatomic.h>
10#include <stdio.h>
11#include <stdlib.h>
8#include <string.h> 12#include <string.h>
9#include <unistd.h> 13#include <unistd.h>
10 14
@@ -149,9 +153,86 @@ void tcp_accept(const int64_t serversocket, struct ot_workstruct *ws) {
149 if (fdstate_generation((int)sock) != generation) 153 if (fdstate_generation((int)sock) != generation)
150 continue; 154 continue;
151 155
152 if (http_adopt(sock, &cookie) == -1) 156 /* Handling it may already have given it a heap home and handed it to
157 another thread, and then it is no longer this frame's to touch */
158 if (fdstate_cookie((int)sock) != &cookie)
159 fdstate_release((int)sock);
160 else if (!http_adopt(sock))
153 http_close(sock); 161 http_close(sock);
154 else 162 else
155 fdstate_release((int)sock); 163 fdstate_release((int)sock);
156 } 164 }
157} 165}
166
167/* One of these per worker, each allocated on its own so that growing the
168 array below never moves the one a running worker holds. */
169struct tcp_worker {
170 pthread_t thread;
171 int64_t sock;
172 _Atomic int left;
173};
174
175static struct tcp_worker **g_workers;
176static size_t g_worker_count;
177
178/* A worker blocks in accept and nowhere else: the connection it is handed is
179 non blocking, so a client that stops talking is given to the event loop
180 rather than keeping this thread. */
181static void *tcp_worker(void *args) {
182 struct tcp_worker *me = args;
183 struct ot_workstruct ws;
184
185 if (workstruct_init(&ws) == -1) {
186 fprintf(stderr, "Warning: a tcp worker could not get its buffers and will not run.\n");
187 atomic_store(&me->left, 1);
188 return NULL;
189 }
190
191 while (g_opentracker_running)
192 tcp_accept(me->sock, &ws);
193
194 workstruct_deinit(&ws);
195 atomic_store(&me->left, 1);
196 return NULL;
197}
198
199void tcp_worker_install(int64_t sock) {
200 struct tcp_worker **grown = realloc(g_workers, (g_worker_count + 1) * sizeof(*g_workers));
201 struct tcp_worker *worker;
202
203 if (!grown || !(worker = calloc(1, sizeof(*worker)))) {
204 fprintf(stderr, "Warning: out of memory installing a tcp worker, this socket stays with the workers already running.\n");
205 g_workers = grown ? grown : g_workers;
206 return;
207 }
208 g_workers = grown;
209 worker->sock = sock;
210
211 socket_set_blocking((int)sock, 1);
212 socket_defer_accept((int)sock);
213
214 if (pthread_create(&worker->thread, NULL, tcp_worker, worker))
215 free(worker);
216 else
217 g_workers[g_worker_count++] = worker;
218}
219
220void tcp_workers_deinit(void) {
221 size_t i;
222
223 for (i = 0; i < g_worker_count; ++i) {
224 struct tcp_worker *worker = g_workers[i];
225
226 /* Repeated because a signal only interrupts a call already under way */
227 while (!atomic_load(&worker->left)) {
228 pthread_kill(worker->thread, OT_SIGNAL_WAKE);
229 usleep(1000);
230 }
231 pthread_join(worker->thread, NULL);
232 free(worker);
233 }
234
235 free(g_workers);
236 g_workers = NULL;
237 g_worker_count = 0;
238}