Bug Summary

File:lwan-thread.c
Warning:line 654, column 25
Assigned value is garbage or undefined

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name lwan-thread.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=all -fmath-errno -fno-rounding-math -masm-verbose -mconstructor-aliases -fno-plt -munwind-tables -target-cpu x86-64 -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/clang/10.0.0 -include /home/buildbot/lwan-worker/clang-analyze/build/lwan-build-config.h -D _FILE_OFFSET_BITS=64 -D _TIME_BITS=64 -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib/missing -I /usr/include/luajit-2.0 -I /usr/include/valgrind -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib -I /home/buildbot/lwan-worker/clang-analyze/build -internal-isystem /usr/local/include -internal-isystem /usr/lib/clang/10.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-free-nonheap-object -std=gnu99 -fdebug-compilation-dir /home/buildbot/lwan-worker/clang-analyze/build/src/lib -ferror-limit 19 -fmessage-length 0 -stack-protector 2 -fgnuc-version=4.2.1 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-output=html -faddrsig -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2020-05-15-143856-71298-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c
1/*
2 * lwan - simple web server
3 * Copyright (c) 2012, 2013 Leandro A. F. Pereira <leandro@hardinfo.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 * USA.
19 */
20
21#define _GNU_SOURCE
22#include <assert.h>
23#include <errno(*__errno_location ()).h>
24#include <fcntl.h>
25#include <pthread.h>
26#include <sched.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/epoll.h>
30#include <sys/ioctl.h>
31#include <sys/socket.h>
32#include <unistd.h>
33
34#if defined(HAVE_EVENTFD)
35#include <sys/eventfd.h>
36#endif
37
38#include "lwan-private.h"
39#include "lwan-tq.h"
40#include "list.h"
41
42static void lwan_strbuf_free_defer(void *data)
43{
44 lwan_strbuf_free((struct lwan_strbuf *)data);
45}
46
47static void graceful_close(struct lwan *l,
48 struct lwan_connection *conn,
49 char buffer[static DEFAULT_BUFFER_SIZE4096])
50{
51 int fd = lwan_connection_get_fd(l, conn);
52
53 while (TIOCOUTQ0x5411) {
54 /* This ioctl isn't probably doing what it says on the tin; the details
55 * are subtle, but it seems to do the trick to allow gracefully closing
56 * the connection in some cases with minimal system calls. */
57 int bytes_waiting;
58 int r = ioctl(fd, TIOCOUTQ0x5411, &bytes_waiting);
59
60 if (!r && !bytes_waiting) /* See note about close(2) below. */
61 return;
62 if (r < 0 && errno(*__errno_location ()) == EINTR4)
63 continue;
64
65 break;
66 }
67
68 if (UNLIKELY(shutdown(fd, SHUT_WR) < 0)__builtin_expect(((shutdown(fd, SHUT_WR) < 0)), (0))) {
69 if (UNLIKELY(errno == ENOTCONN)__builtin_expect((((*__errno_location ()) == 107)), (0)))
70 return;
71 }
72
73 for (int tries = 0; tries < 20; tries++) {
74 ssize_t r = read(fd, buffer, DEFAULT_BUFFER_SIZE4096);
75
76 if (!r)
77 break;
78
79 if (r < 0) {
80 switch (errno(*__errno_location ())) {
81 case EINTR4:
82 continue;
83 case EAGAIN11:
84 coro_yield(conn->coro, CONN_CORO_WANT_READ);
85 continue;
86 default:
87 return;
88 }
89 }
90
91 coro_yield(conn->coro, CONN_CORO_YIELD);
92 }
93
94 /* close(2) will be called when the coroutine yields with CONN_CORO_ABORT */
95}
96
97__attribute__((noreturn)) static int process_request_coro(struct coro *coro,
98 void *data)
99{
100 /* NOTE: This function should not return; coro_yield should be used
101 * instead. This ensures the storage for `strbuf` is alive when the
102 * coroutine ends and lwan_strbuf_free() is called. */
103 struct lwan_connection *conn = data;
104 struct lwan *lwan = conn->thread->lwan;
105 int fd = lwan_connection_get_fd(lwan, conn);
106 enum lwan_request_flags flags = lwan->config.request_flags;
107 struct lwan_strbuf strbuf = LWAN_STRBUF_STATIC_INIT(struct lwan_strbuf) { .buffer = "" };
108 char request_buffer[DEFAULT_BUFFER_SIZE4096];
109 struct lwan_value buffer = {.value = request_buffer, .len = 0};
110 char *next_request = NULL((void*)0);
111 struct lwan_proxy proxy;
112
113 coro_defer(coro, lwan_strbuf_free_defer, &strbuf);
114
115 const size_t init_gen = 1; /* 1 call to coro_defer() */
116 assert(init_gen == coro_deferred_get_generation(coro))((void) sizeof ((init_gen == coro_deferred_get_generation(coro
)) ? 1 : 0), __extension__ ({ if (init_gen == coro_deferred_get_generation
(coro)) ; else __assert_fail ("init_gen == coro_deferred_get_generation(coro)"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 116, __extension__ __PRETTY_FUNCTION__); }))
;
117
118 while (true1) {
119 struct lwan_request request = {.conn = conn,
120 .fd = fd,
121 .response = {.buffer = &strbuf},
122 .flags = flags,
123 .proxy = &proxy};
124
125 next_request =
126 lwan_process_request(lwan, &request, &buffer, next_request);
127
128 if (coro_deferred_get_generation(coro) > ((2 * LWAN_ARRAY_INCREMENT16) / 3)) {
129 /* Batch execution of coro_defers() up to 2/3 LWAN_ARRAY_INCREMENT times,
130 * to avoid moving deferred array to heap in most cases. (This is to give
131 * some slack to the next request being processed by this coro.) */
132 coro_deferred_run(coro, init_gen);
133 }
134
135 if (LIKELY(conn->flags & CONN_IS_KEEP_ALIVE)__builtin_expect((!!(conn->flags & CONN_IS_KEEP_ALIVE)
), (1))
) {
136 if (next_request && *next_request) {
137 conn->flags |= CONN_CORK;
138 coro_yield(coro, CONN_CORO_WANT_WRITE);
139 } else {
140 conn->flags &= ~CONN_CORK;
141 coro_yield(coro, CONN_CORO_WANT_READ);
142 }
143 } else {
144 graceful_close(lwan, conn, request_buffer);
145 break;
146 }
147
148 lwan_strbuf_reset(&strbuf);
149
150 /* Only allow flags from config. */
151 flags = request.flags & (REQUEST_PROXIED | REQUEST_ALLOW_CORS);
152 }
153
154 coro_yield(coro, CONN_CORO_ABORT);
155 __builtin_unreachable();
156}
157
158#undef REQUEST_FLAG
159
160static ALWAYS_INLINEinline __attribute__((always_inline)) uint32_t
161conn_flags_to_epoll_events(enum lwan_connection_flags flags)
162{
163 static const uint32_t map[CONN_EVENTS_MASK + 1] = {
164 [0 /* Suspended (timer or await) */] = EPOLLRDHUPEPOLLRDHUP,
165 [CONN_EVENTS_WRITE] = EPOLLOUTEPOLLOUT | EPOLLRDHUPEPOLLRDHUP,
166 [CONN_EVENTS_READ] = EPOLLINEPOLLIN | EPOLLRDHUPEPOLLRDHUP,
167 [CONN_EVENTS_READ_WRITE] = EPOLLINEPOLLIN | EPOLLOUTEPOLLOUT | EPOLLRDHUPEPOLLRDHUP,
168 [CONN_EVENTS_ASYNC_READ] = EPOLLETEPOLLET | EPOLLINEPOLLIN | EPOLLRDHUPEPOLLRDHUP | EPOLLONESHOTEPOLLONESHOT,
169 [CONN_EVENTS_ASYNC_WRITE] = EPOLLETEPOLLET | EPOLLOUTEPOLLOUT | EPOLLRDHUPEPOLLRDHUP | EPOLLONESHOTEPOLLONESHOT,
170 [CONN_EVENTS_ASYNC_READ_WRITE] = EPOLLETEPOLLET | EPOLLINEPOLLIN | EPOLLOUTEPOLLOUT | EPOLLRDHUPEPOLLRDHUP | EPOLLONESHOTEPOLLONESHOT,
171 };
172
173 return map[flags & CONN_EVENTS_MASK];
174}
175
176#if defined(__linux__1)
177# define CONN_EVENTS_RESUMECONN_EVENTS_READ_WRITE CONN_EVENTS_READ_WRITE
178#else
179/* Kqueue doesn't like when you filter on both read and write, so
180 * wait only on write when resuming a coro suspended by a timer.
181 * The I/O wrappers should yield if trying to read without anything
182 * in the buffer, changing the filter to only read, so this is OK. */
183# define CONN_EVENTS_RESUMECONN_EVENTS_READ_WRITE CONN_EVENTS_WRITE
184#endif
185
186static void update_epoll_flags(int fd,
187 struct lwan_connection *conn,
188 int epoll_fd,
189 enum lwan_connection_coro_yield yield_result)
190{
191 static const enum lwan_connection_flags or_mask[CONN_CORO_MAX] = {
192 [CONN_CORO_YIELD] = 0,
193
194 [CONN_CORO_WANT_READ_WRITE] = CONN_EVENTS_READ_WRITE,
195 [CONN_CORO_WANT_READ] = CONN_EVENTS_READ,
196 [CONN_CORO_WANT_WRITE] = CONN_EVENTS_WRITE,
197
198 /* While the coro is suspended, we're not interested in either EPOLLIN
199 * or EPOLLOUT events. We still want to track this fd in epoll, though,
200 * so unset both so that only EPOLLRDHUP (plus the implicitly-set ones)
201 * are set. */
202 [CONN_CORO_SUSPEND_TIMER] = CONN_SUSPENDED_TIMER,
203 [CONN_CORO_SUSPEND_ASYNC_AWAIT] = CONN_SUSPENDED_ASYNC_AWAIT,
204
205 /* Either EPOLLIN or EPOLLOUT have to be set here. There's no need to
206 * know which event, because they were both cleared when the coro was
207 * suspended. So set both flags here. This works because EPOLLET isn't
208 * used. */
209 [CONN_CORO_RESUME] = CONN_EVENTS_RESUMECONN_EVENTS_READ_WRITE,
210
211 [CONN_CORO_ASYNC_AWAIT_READ] = CONN_EVENTS_ASYNC_READ,
212 [CONN_CORO_ASYNC_AWAIT_WRITE] = CONN_EVENTS_ASYNC_WRITE,
213 [CONN_CORO_ASYNC_AWAIT_READ_WRITE] = CONN_EVENTS_ASYNC_READ_WRITE,
214 };
215 static const enum lwan_connection_flags and_mask[CONN_CORO_MAX] = {
216 [CONN_CORO_YIELD] = ~0,
217
218 [CONN_CORO_WANT_READ_WRITE] = ~0,
219 [CONN_CORO_WANT_READ] = ~CONN_EVENTS_WRITE,
220 [CONN_CORO_WANT_WRITE] = ~CONN_EVENTS_READ,
221
222 [CONN_CORO_SUSPEND_TIMER] = ~(CONN_EVENTS_READ_WRITE | CONN_SUSPENDED_ASYNC_AWAIT),
223 [CONN_CORO_SUSPEND_ASYNC_AWAIT] = ~(CONN_EVENTS_READ_WRITE | CONN_SUSPENDED_TIMER),
224 [CONN_CORO_RESUME] = ~CONN_SUSPENDED,
225
226 [CONN_CORO_ASYNC_AWAIT_READ] = ~0,
227 [CONN_CORO_ASYNC_AWAIT_WRITE] = ~0,
228 [CONN_CORO_ASYNC_AWAIT_READ_WRITE] = ~0,
229 };
230 enum lwan_connection_flags prev_flags = conn->flags;
231
232 conn->flags |= or_mask[yield_result];
233 conn->flags &= and_mask[yield_result];
234
235 if (conn->flags == prev_flags)
236 return;
237
238 struct epoll_event event = {
239 .events = conn_flags_to_epoll_events(conn->flags),
240 .data.ptr = conn,
241 };
242
243 if (UNLIKELY(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0)__builtin_expect(((epoll_ctl(epoll_fd, 3, fd, &event) <
0)), (0))
)
244 lwan_status_perror("epoll_ctl")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 244, __FUNCTION__, "epoll_ctl")
;
245}
246
247static ALWAYS_INLINEinline __attribute__((always_inline)) void resume_coro(struct timeout_queue *tq,
248 struct lwan_connection *conn,
249 int epoll_fd)
250{
251 assert(conn->coro)((void) sizeof ((conn->coro) ? 1 : 0), __extension__ ({ if
(conn->coro) ; else __assert_fail ("conn->coro", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 251, __extension__ __PRETTY_FUNCTION__); }))
;
252
253 int64_t from_coro = coro_resume(conn->coro);
254 enum lwan_connection_coro_yield yield_result = from_coro & 0xffffffff;
255
256 if (UNLIKELY(yield_result == CONN_CORO_ABORT)__builtin_expect(((yield_result == CONN_CORO_ABORT)), (0)))
257 return timeout_queue_expire(tq, conn);
258
259 if (UNLIKELY(yield_result >= CONN_CORO_ASYNC)__builtin_expect(((yield_result >= CONN_CORO_ASYNC)), (0))) {
260 assert(yield_result >= CONN_CORO_ASYNC_AWAIT_READ &&((void) sizeof ((yield_result >= CONN_CORO_ASYNC_AWAIT_READ
&& yield_result <= CONN_CORO_ASYNC_RESUME) ? 1 : 0
), __extension__ ({ if (yield_result >= CONN_CORO_ASYNC_AWAIT_READ
&& yield_result <= CONN_CORO_ASYNC_RESUME) ; else
__assert_fail ("yield_result >= CONN_CORO_ASYNC_AWAIT_READ && yield_result <= CONN_CORO_ASYNC_RESUME"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 261, __extension__ __PRETTY_FUNCTION__); }))
261 yield_result <= CONN_CORO_ASYNC_RESUME)((void) sizeof ((yield_result >= CONN_CORO_ASYNC_AWAIT_READ
&& yield_result <= CONN_CORO_ASYNC_RESUME) ? 1 : 0
), __extension__ ({ if (yield_result >= CONN_CORO_ASYNC_AWAIT_READ
&& yield_result <= CONN_CORO_ASYNC_RESUME) ; else
__assert_fail ("yield_result >= CONN_CORO_ASYNC_AWAIT_READ && yield_result <= CONN_CORO_ASYNC_RESUME"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 261, __extension__ __PRETTY_FUNCTION__); }))
;
262
263 static const enum lwan_connection_flags to_connection_flags[] = {
264 [CONN_CORO_ASYNC_AWAIT_READ] = CONN_EVENTS_ASYNC_READ,
265 [CONN_CORO_ASYNC_AWAIT_WRITE] = CONN_EVENTS_ASYNC_WRITE,
266 [CONN_CORO_ASYNC_AWAIT_READ_WRITE] = CONN_EVENTS_ASYNC_READ_WRITE,
267 [CONN_CORO_ASYNC_RESUME] = 0,
268 };
269 struct epoll_event event = {
270 .events =
271 conn_flags_to_epoll_events(to_connection_flags[yield_result]),
272 .data.ptr = conn,
273 };
274 int await_fd = (int)((uint64_t)from_coro >> 32);
275 int op;
276
277 assert(event.events != 0)((void) sizeof ((event.events != 0) ? 1 : 0), __extension__ (
{ if (event.events != 0) ; else __assert_fail ("event.events != 0"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 277, __extension__ __PRETTY_FUNCTION__); }))
;
278 assert(await_fd >= 0)((void) sizeof ((await_fd >= 0) ? 1 : 0), __extension__ ({
if (await_fd >= 0) ; else __assert_fail ("await_fd >= 0"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 278, __extension__ __PRETTY_FUNCTION__); }))
;
279 assert(await_fd <= tq->lwan->thread.count * tq->lwan->thread.max_fd)((void) sizeof ((await_fd <= tq->lwan->thread.count *
tq->lwan->thread.max_fd) ? 1 : 0), __extension__ ({ if
(await_fd <= tq->lwan->thread.count * tq->lwan->
thread.max_fd) ; else __assert_fail ("await_fd <= tq->lwan->thread.count * tq->lwan->thread.max_fd"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 279, __extension__ __PRETTY_FUNCTION__); }))
;
280
281 if (yield_result == CONN_CORO_ASYNC_RESUME) {
282 op = EPOLL_CTL_DEL2;
283 yield_result = CONN_CORO_RESUME;
284 } else {
285 op = EPOLL_CTL_ADD1;
286 yield_result = CONN_CORO_SUSPEND_ASYNC_AWAIT;
287 }
288
289 if (UNLIKELY(epoll_ctl(epoll_fd, op, await_fd, &event) < 0)__builtin_expect(((epoll_ctl(epoll_fd, op, await_fd, &event
) < 0)), (0))
)
290 lwan_status_perror("epoll_ctl")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 290, __FUNCTION__, "epoll_ctl")
;
291 }
292
293 return update_epoll_flags(lwan_connection_get_fd(tq->lwan, conn), conn,
294 epoll_fd, yield_result);
295}
296
297static void update_date_cache(struct lwan_thread *thread)
298{
299 time_t now = time(NULL((void*)0));
300
301 lwan_format_rfc_time(now, thread->date.date);
302 lwan_format_rfc_time(now + (time_t)thread->lwan->config.expires,
303 thread->date.expires);
304}
305
306static ALWAYS_INLINEinline __attribute__((always_inline)) void spawn_coro(struct lwan_connection *conn,
307 struct coro_switcher *switcher,
308 struct timeout_queue *tq)
309{
310 struct lwan_thread *t = conn->thread;
311
312 assert(!conn->coro)((void) sizeof ((!conn->coro) ? 1 : 0), __extension__ ({ if
(!conn->coro) ; else __assert_fail ("!conn->coro", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 312, __extension__ __PRETTY_FUNCTION__); }))
;
313 assert(t)((void) sizeof ((t) ? 1 : 0), __extension__ ({ if (t) ; else __assert_fail
("t", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 313, __extension__ __PRETTY_FUNCTION__); }))
;
314 assert((uintptr_t)t >= (uintptr_t)tq->lwan->thread.threads)((void) sizeof (((uintptr_t)t >= (uintptr_t)tq->lwan->
thread.threads) ? 1 : 0), __extension__ ({ if ((uintptr_t)t >=
(uintptr_t)tq->lwan->thread.threads) ; else __assert_fail
("(uintptr_t)t >= (uintptr_t)tq->lwan->thread.threads"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 314, __extension__ __PRETTY_FUNCTION__); }))
;
315 assert((uintptr_t)t <((void) sizeof (((uintptr_t)t < (uintptr_t)(tq->lwan->
thread.threads + tq->lwan->thread.count)) ? 1 : 0), __extension__
({ if ((uintptr_t)t < (uintptr_t)(tq->lwan->thread.
threads + tq->lwan->thread.count)) ; else __assert_fail
("(uintptr_t)t < (uintptr_t)(tq->lwan->thread.threads + tq->lwan->thread.count)"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 316, __extension__ __PRETTY_FUNCTION__); }))
316 (uintptr_t)(tq->lwan->thread.threads + tq->lwan->thread.count))((void) sizeof (((uintptr_t)t < (uintptr_t)(tq->lwan->
thread.threads + tq->lwan->thread.count)) ? 1 : 0), __extension__
({ if ((uintptr_t)t < (uintptr_t)(tq->lwan->thread.
threads + tq->lwan->thread.count)) ; else __assert_fail
("(uintptr_t)t < (uintptr_t)(tq->lwan->thread.threads + tq->lwan->thread.count)"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 316, __extension__ __PRETTY_FUNCTION__); }))
;
317
318 *conn = (struct lwan_connection) {
319 .coro = coro_new(switcher, process_request_coro, conn),
320 .flags = CONN_EVENTS_READ,
321 .time_to_expire = tq->current_time + tq->move_to_last_bump,
322 .thread = t,
323 };
324 if (UNLIKELY(!conn->coro)__builtin_expect(((!conn->coro)), (0))) {
325 conn->flags = 0;
326 lwan_status_error("Could not create coroutine")lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 326, __FUNCTION__, "Could not create coroutine")
;
327 return;
328 }
329
330 timeout_queue_insert(tq, conn);
331}
332
333static void accept_nudge(int pipe_fd,
334 struct lwan_thread *t,
335 struct lwan_connection *conns,
336 struct timeout_queue *tq,
337 struct coro_switcher *switcher,
338 int epoll_fd)
339{
340 uint64_t event;
341 int new_fd;
342
343 /* Errors are ignored here as pipe_fd serves just as a way to wake the
344 * thread from epoll_wait(). It's fine to consume the queue at this
345 * point, regardless of the error type. */
346 (void)read(pipe_fd, &event, sizeof(event));
347
348 while (spsc_queue_pop(&t->pending_fds, &new_fd)) {
349 struct lwan_connection *conn = &conns[new_fd];
350 struct epoll_event ev = {
351 .data.ptr = conn,
352 .events = conn_flags_to_epoll_events(CONN_EVENTS_READ),
353 };
354
355 if (LIKELY(!epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_fd, &ev))__builtin_expect((!!(!epoll_ctl(epoll_fd, 1, new_fd, &ev)
)), (1))
)
356 spawn_coro(conn, switcher, tq);
357 }
358
359 timeouts_add(t->wheel, &tq->timeout, 1000);
360}
361
362static bool_Bool process_pending_timers(struct timeout_queue *tq,
363 struct lwan_thread *t,
364 int epoll_fd)
365{
366 struct timeout *timeout;
367 bool_Bool should_expire_timers = false0;
368
369 while ((timeout = timeouts_get(t->wheel))) {
370 struct lwan_request *request;
371
372 if (timeout == &tq->timeout) {
373 should_expire_timers = true1;
374 continue;
375 }
376
377 request = container_of(timeout, struct lwan_request, timeout)((struct lwan_request *) ((char *)(timeout) - __builtin_offsetof
(struct lwan_request, timeout)) + ((typeof(*(timeout)) *)0 !=
(typeof(((struct lwan_request *)0)->timeout) *)0))
;
378
379 update_epoll_flags(request->fd, request->conn, epoll_fd,
380 CONN_CORO_RESUME);
381 }
382
383 if (should_expire_timers) {
384 timeout_queue_expire_waiting(tq);
385
386 /* tq timeout expires every 1000ms if there are connections, so
387 * update the date cache at this point as well. */
388 update_date_cache(t);
389
390 if (!timeout_queue_empty(tq)) {
391 timeouts_add(t->wheel, &tq->timeout, 1000);
392 return true1;
393 }
394
395 timeouts_del(t->wheel, &tq->timeout);
396 }
397
398 return false0;
399}
400
401static int
402turn_timer_wheel(struct timeout_queue *tq, struct lwan_thread *t, int epoll_fd)
403{
404 timeout_t wheel_timeout;
405 struct timespec now;
406
407 if (UNLIKELY(clock_gettime(monotonic_clock_id, &now) < 0)__builtin_expect(((clock_gettime(monotonic_clock_id, &now
) < 0)), (0))
)
408 lwan_status_critical("Could not get monotonic time")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 408, __FUNCTION__, "Could not get monotonic time")
;
409
410 timeouts_update(t->wheel,
411 (timeout_t)(now.tv_sec * 1000 + now.tv_nsec / 1000000));
412
413 wheel_timeout = timeouts_timeout(t->wheel);
414 if (UNLIKELY((int64_t)wheel_timeout < 0)__builtin_expect((((int64_t)wheel_timeout < 0)), (0)))
415 goto infinite_timeout;
416
417 if (wheel_timeout == 0) {
418 if (!process_pending_timers(tq, t, epoll_fd))
419 goto infinite_timeout;
420
421 wheel_timeout = timeouts_timeout(t->wheel);
422 if (wheel_timeout == 0)
423 goto infinite_timeout;
424 }
425
426 return (int)wheel_timeout;
427
428infinite_timeout:
429 return -1;
430}
431
432static void *thread_io_loop(void *data)
433{
434 struct lwan_thread *t = data;
435 int epoll_fd = t->epoll_fd;
436 const int read_pipe_fd = t->pipe_fd[0];
437 const int max_events = LWAN_MIN((int)t->lwan->thread.max_fd, 1024)({ const __typeof__(((int)t->lwan->thread.max_fd) + 0) lwan_tmp_id4
= ((int)t->lwan->thread.max_fd); const __typeof__((1024
) + 0) lwan_tmp_id5 = (1024); lwan_tmp_id4 > lwan_tmp_id5 ?
lwan_tmp_id5 : lwan_tmp_id4; })
;
438 struct lwan *lwan = t->lwan;
439 struct epoll_event *events;
440 struct coro_switcher switcher;
441 struct timeout_queue tq;
442
443 lwan_status_debug("Worker thread #%zd starting",lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 444, __FUNCTION__, "Worker thread #%zd starting", t - t->
lwan->thread.threads + 1)
444 t - t->lwan->thread.threads + 1)lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 444, __FUNCTION__, "Worker thread #%zd starting", t - t->
lwan->thread.threads + 1)
;
445 lwan_set_thread_name("worker");
446
447 events = calloc((size_t)max_events, sizeof(*events));
448 if (UNLIKELY(!events)__builtin_expect(((!events)), (0)))
449 lwan_status_critical("Could not allocate memory for events")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 449, __FUNCTION__, "Could not allocate memory for events")
;
450
451 update_date_cache(t);
452
453 timeout_queue_init(&tq, lwan);
454
455 pthread_barrier_wait(&lwan->thread.barrier);
456
457 for (;;) {
458 int timeout = turn_timer_wheel(&tq, t, epoll_fd);
459 int n_fds = epoll_wait(epoll_fd, events, max_events, timeout);
460
461 if (UNLIKELY(n_fds < 0)__builtin_expect(((n_fds < 0)), (0))) {
462 if (errno(*__errno_location ()) == EBADF9 || errno(*__errno_location ()) == EINVAL22)
463 break;
464 continue;
465 }
466
467 for (struct epoll_event *event = events; n_fds--; event++) {
468 struct lwan_connection *conn;
469
470 if (UNLIKELY(!event->data.ptr)__builtin_expect(((!event->data.ptr)), (0))) {
471 accept_nudge(read_pipe_fd, t, lwan->conns, &tq, &switcher,
472 epoll_fd);
473 continue;
474 }
475
476 conn = event->data.ptr;
477
478 if (UNLIKELY(event->events & (EPOLLRDHUP | EPOLLHUP))__builtin_expect(((event->events & (EPOLLRDHUP | EPOLLHUP
))), (0))
) {
479 timeout_queue_expire(&tq, conn);
480 continue;
481 }
482
483 resume_coro(&tq, conn, epoll_fd);
484 timeout_queue_move_to_last(&tq, conn);
485 }
486 }
487
488 pthread_barrier_wait(&lwan->thread.barrier);
489
490 timeout_queue_expire_all(&tq);
491 free(events);
492
493 return NULL((void*)0);
494}
495
496static void create_thread(struct lwan *l, struct lwan_thread *thread,
497 const size_t n_queue_fds)
498{
499 int ignore;
500 pthread_attr_t attr;
501
502 memset(thread, 0, sizeof(*thread));
503 thread->lwan = l;
504
505 thread->wheel = timeouts_open(&ignore);
506 if (!thread->wheel)
507 lwan_status_critical("Could not create timer wheel")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 507, __FUNCTION__, "Could not create timer wheel")
;
508
509 if ((thread->epoll_fd = epoll_create1(EPOLL_CLOEXECEPOLL_CLOEXEC)) < 0)
510 lwan_status_critical_perror("epoll_create")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 510, __FUNCTION__, "epoll_create")
;
511
512 if (pthread_attr_init(&attr))
513 lwan_status_critical_perror("pthread_attr_init")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 513, __FUNCTION__, "pthread_attr_init")
;
514
515 if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEMPTHREAD_SCOPE_SYSTEM))
516 lwan_status_critical_perror("pthread_attr_setscope")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 516, __FUNCTION__, "pthread_attr_setscope")
;
517
518 if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLEPTHREAD_CREATE_JOINABLE))
519 lwan_status_critical_perror("pthread_attr_setdetachstate")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 519, __FUNCTION__, "pthread_attr_setdetachstate")
;
520
521#if defined(HAVE_EVENTFD)
522 int efd = eventfd(0, EFD_NONBLOCKEFD_NONBLOCK | EFD_SEMAPHOREEFD_SEMAPHORE | EFD_CLOEXECEFD_CLOEXEC);
523 if (efd < 0)
524 lwan_status_critical_perror("eventfd")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 524, __FUNCTION__, "eventfd")
;
525
526 thread->pipe_fd[0] = thread->pipe_fd[1] = efd;
527#else
528 if (pipe2(thread->pipe_fd, O_NONBLOCK04000 | O_CLOEXEC02000000) < 0)
529 lwan_status_critical_perror("pipe")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 529, __FUNCTION__, "pipe")
;
530#endif
531
532 struct epoll_event event = { .events = EPOLLINEPOLLIN, .data.ptr = NULL((void*)0) };
533 if (epoll_ctl(thread->epoll_fd, EPOLL_CTL_ADD1, thread->pipe_fd[0], &event) < 0)
534 lwan_status_critical_perror("epoll_ctl")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 534, __FUNCTION__, "epoll_ctl")
;
535
536 if (pthread_create(&thread->self, &attr, thread_io_loop, thread))
537 lwan_status_critical_perror("pthread_create")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 537, __FUNCTION__, "pthread_create")
;
538
539 if (pthread_attr_destroy(&attr))
540 lwan_status_critical_perror("pthread_attr_destroy")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 540, __FUNCTION__, "pthread_attr_destroy")
;
541
542 if (spsc_queue_init(&thread->pending_fds, n_queue_fds) < 0) {
543 lwan_status_critical("Could not initialize pending fd "lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 544, __FUNCTION__, "Could not initialize pending fd " "queue width %zu elements"
, n_queue_fds)
544 "queue width %zu elements", n_queue_fds)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 544, __FUNCTION__, "Could not initialize pending fd " "queue width %zu elements"
, n_queue_fds)
;
545 }
546}
547
548void lwan_thread_nudge(struct lwan_thread *t)
549{
550 uint64_t event = 1;
551
552 if (UNLIKELY(write(t->pipe_fd[1], &event, sizeof(event)) < 0)__builtin_expect(((write(t->pipe_fd[1], &event, sizeof
(event)) < 0)), (0))
)
553 lwan_status_perror("write")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 553, __FUNCTION__, "write")
;
554}
555
556void lwan_thread_add_client(struct lwan_thread *t, int fd)
557{
558 for (int i = 0; i < 10; i++) {
559 bool_Bool pushed = spsc_queue_push(&t->pending_fds, fd);
560
561 if (LIKELY(pushed)__builtin_expect((!!(pushed)), (1)))
562 return;
563
564 /* Queue is full; nudge the thread to consume it. */
565 lwan_thread_nudge(t);
566 }
567
568 lwan_status_error("Dropping connection %d", fd)lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 568, __FUNCTION__, "Dropping connection %d", fd)
;
569 /* FIXME: send "busy" response now, even without receiving request? */
570 close(fd);
571}
572
573#if defined(__linux__1) && defined(__x86_64__1)
574static bool_Bool read_cpu_topology(struct lwan *l, uint32_t siblings[])
575{
576 char path[PATH_MAX4096];
577
578 for (unsigned int i = 0; i < l->n_cpus; i++) {
579 FILE *sib;
580 uint32_t id, sibling;
581 char separator;
582
583 snprintf(path, sizeof(path),
584 "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
585 i);
586
587 sib = fopen(path, "re");
588 if (!sib) {
589 lwan_status_warning("Could not open `%s` to determine CPU topology",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 590, __FUNCTION__, "Could not open `%s` to determine CPU topology"
, path)
590 path)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 590, __FUNCTION__, "Could not open `%s` to determine CPU topology"
, path)
;
591 return false0;
592 }
593
594 switch (fscanf(sib, "%u%c%u", &id, &separator, &sibling)) {
595 case 2: /* No SMT */
596 siblings[i] = id;
597 break;
598 case 3: /* SMT */
599 if (!(separator == ',' || separator == '-')) {
600 lwan_status_critical("Expecting either ',' or '-' for sibling separator")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 600, __FUNCTION__, "Expecting either ',' or '-' for sibling separator"
)
;
601 __builtin_unreachable();
602 }
603
604 siblings[i] = sibling;
605 break;
606 default:
607 lwan_status_critical("%s has invalid format", path)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 607, __FUNCTION__, "%s has invalid format", path)
;
608 __builtin_unreachable();
609 }
610
611
612 fclose(sib);
613 }
614
615 return true1;
616}
617
618static void
619siblings_to_schedtbl(struct lwan *l, uint32_t siblings[], uint32_t schedtbl[])
620{
621 int *seen = alloca(l->n_cpus * sizeof(int))__builtin_alloca (l->n_cpus * sizeof(int));
622 int n_schedtbl = 0;
623
624 for (uint32_t i = 0; i < l->n_cpus; i++)
14
Assuming 'i' is < field 'n_cpus'
15
Loop condition is true. Entering loop body
16
Assuming 'i' is < field 'n_cpus'
17
Loop condition is true. Entering loop body
18
Assuming 'i' is < field 'n_cpus'
19
Loop condition is true. Entering loop body
20
Assuming 'i' is >= field 'n_cpus'
21
Loop condition is false. Execution continues on line 627
625 seen[i] = -1;
626
627 for (uint32_t i = 0; i < l->n_cpus; i++) {
22
Loop condition is true. Entering loop body
25
Loop condition is true. Entering loop body
28
Loop condition is true. Entering loop body
31
Loop condition is false. Execution continues on line 636
628 if (seen[siblings[i]] < 0) {
23
Assuming the condition is false
24
Taking false branch
26
Assuming the condition is true
27
Taking true branch
29
Assuming the condition is true
30
Taking true branch
629 seen[siblings[i]] = (int)i;
630 } else {
631 schedtbl[n_schedtbl++] = (uint32_t)seen[siblings[i]];
632 schedtbl[n_schedtbl++] = i;
633 }
634 }
635
636 if (!n_schedtbl
31.1
'n_schedtbl' is 2
)
32
Taking false branch
637 memcpy(schedtbl, seen, l->n_cpus * sizeof(int));
638}
639
640static void
641topology_to_schedtbl(struct lwan *l, uint32_t schedtbl[], uint32_t n_threads)
642{
643 uint32_t *siblings = alloca(l->n_cpus * sizeof(uint32_t))__builtin_alloca (l->n_cpus * sizeof(uint32_t));
644
645 if (!read_cpu_topology(l, siblings)) {
11
Assuming the condition is false
12
Taking false branch
646 for (uint32_t i = 0; i < n_threads; i++)
647 schedtbl[i] = (i / 2) % l->thread.count;
648 } else {
649 uint32_t *affinity = alloca(l->n_cpus * sizeof(uint32_t))__builtin_alloca (l->n_cpus * sizeof(uint32_t));
650
651 siblings_to_schedtbl(l, siblings, affinity);
13
Calling 'siblings_to_schedtbl'
33
Returning from 'siblings_to_schedtbl'
652
653 for (uint32_t i = 0; i < n_threads; i++)
34
Assuming 'i' is < 'n_threads'
35
Loop condition is true. Entering loop body
36
Assuming 'i' is < 'n_threads'
37
Loop condition is true. Entering loop body
38
Assuming 'i' is < 'n_threads'
39
Loop condition is true. Entering loop body
654 schedtbl[i] = affinity[i % l->n_cpus];
40
Assigned value is garbage or undefined
655 }
656}
657
658static void
659adjust_threads_affinity(struct lwan *l, uint32_t *schedtbl, uint32_t mask)
660{
661 for (uint32_t i = 0; i < l->thread.count; i++) {
662 cpu_set_t set;
663
664 CPU_ZERO(&set)do __builtin_memset (&set, '\0', sizeof (cpu_set_t)); while
(0)
;
665 CPU_SET(schedtbl[i & mask], &set)(__extension__ ({ size_t __cpu = (schedtbl[i & mask]); __cpu
/ 8 < (sizeof (cpu_set_t)) ? (((__cpu_mask *) ((&set)
->__bits))[((__cpu) / (8 * sizeof (__cpu_mask)))] |= ((__cpu_mask
) 1 << ((__cpu) % (8 * sizeof (__cpu_mask))))) : 0; }))
;
666
667 if (pthread_setaffinity_np(l->thread.threads[i].self, sizeof(set),
668 &set))
669 lwan_status_warning("Could not set affinity for thread %d", i)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 669, __FUNCTION__, "Could not set affinity for thread %d", i
)
;
670 }
671}
672#elif defined(__x86_64__1)
673static void
674topology_to_schedtbl(struct lwan *l, uint32_t schedtbl[], uint32_t n_threads)
675{
676 for (uint32_t i = 0; i < n_threads; i++)
677 schedtbl[i] = (i / 2) % l->thread.count;
678}
679
680static void
681adjust_threads_affinity(struct lwan *l, uint32_t *schedtbl, uint32_t n)
682{
683}
684#endif
685
686void lwan_thread_init(struct lwan *l)
687{
688 if (pthread_barrier_init(&l->thread.barrier, NULL((void*)0),
1
Assuming the condition is false
2
Taking false branch
689 (unsigned)l->thread.count + 1))
690 lwan_status_critical("Could not create barrier")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 690, __FUNCTION__, "Could not create barrier")
;
691
692 lwan_status_debug("Initializing threads")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 692, __FUNCTION__, "Initializing threads")
;
693
694 l->thread.threads =
695 calloc((size_t)l->thread.count, sizeof(struct lwan_thread));
696 if (!l->thread.threads)
3
Assuming field 'threads' is non-null
4
Taking false branch
697 lwan_status_critical("Could not allocate memory for threads")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 697, __FUNCTION__, "Could not allocate memory for threads")
;
698
699 const size_t n_queue_fds = LWAN_MIN(l->thread.max_fd / l->thread.count,({ const __typeof__((l->thread.max_fd / l->thread.count
) + 0) lwan_tmp_id6 = (l->thread.max_fd / l->thread.count
); const __typeof__(((size_t)(2 * lwan_socket_get_backlog_size
())) + 0) lwan_tmp_id7 = ((size_t)(2 * lwan_socket_get_backlog_size
())); lwan_tmp_id6 > lwan_tmp_id7 ? lwan_tmp_id7 : lwan_tmp_id6
; })
5
Assuming 'lwan_tmp_id2' is <= 'lwan_tmp_id3'
6
'?' condition is false
700 (size_t)(2 * lwan_socket_get_backlog_size()))({ const __typeof__((l->thread.max_fd / l->thread.count
) + 0) lwan_tmp_id6 = (l->thread.max_fd / l->thread.count
); const __typeof__(((size_t)(2 * lwan_socket_get_backlog_size
())) + 0) lwan_tmp_id7 = ((size_t)(2 * lwan_socket_get_backlog_size
())); lwan_tmp_id6 > lwan_tmp_id7 ? lwan_tmp_id7 : lwan_tmp_id6
; })
;
701 lwan_status_debug("Pending client file descriptor queue has %zu items", n_queue_fds)lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 701, __FUNCTION__, "Pending client file descriptor queue has %zu items"
, n_queue_fds)
;
702 for (unsigned int i = 0; i
6.1
'i' is < field 'count'
< l->thread.count
; i++)
7
Loop condition is true. Entering loop body
8
Assuming 'i' is >= field 'count'
9
Loop condition is false. Execution continues on line 705
703 create_thread(l, &l->thread.threads[i], n_queue_fds);
704
705 const unsigned int total_conns = l->thread.max_fd * l->thread.count;
706#ifdef __x86_64__1
707 static_assert(sizeof(struct lwan_connection) == 32,extern int (*__Static_assert_function (void)) [!!sizeof (struct
{ int __error_if_negative: (sizeof(struct lwan_connection) ==
32) ? 2 : -1; })]
708 "Two connections per cache line")extern int (*__Static_assert_function (void)) [!!sizeof (struct
{ int __error_if_negative: (sizeof(struct lwan_connection) ==
32) ? 2 : -1; })]
;
709 /*
710 * Pre-schedule each file descriptor, to reduce some operations in the
711 * fast path.
712 *
713 * Since struct lwan_connection is guaranteed to be 32-byte long, two of
714 * them can fill up a cache line. Assume siblings share cache lines and
715 * use the CPU topology to group two connections per cache line in such
716 * a way that false sharing is avoided.
717 */
718 uint32_t n_threads = (uint32_t)lwan_nextpow2((size_t)((l->thread.count - 1) * 2));
719 uint32_t *schedtbl = alloca(n_threads * sizeof(uint32_t))__builtin_alloca (n_threads * sizeof(uint32_t));
720
721 topology_to_schedtbl(l, schedtbl, n_threads);
10
Calling 'topology_to_schedtbl'
722
723 n_threads--; /* Transform count into mask for AND below */
724 adjust_threads_affinity(l, schedtbl, n_threads);
725 for (unsigned int i = 0; i < total_conns; i++)
726 l->conns[i].thread = &l->thread.threads[schedtbl[i & n_threads]];
727#else
728 for (unsigned int i = 0; i < total_conns; i++)
729 l->conns[i].thread = &l->thread.threads[i % l->thread.count];
730#endif
731
732 pthread_barrier_wait(&l->thread.barrier);
733
734 lwan_status_debug("Worker threads created and ready to serve")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 734, __FUNCTION__, "Worker threads created and ready to serve"
)
;
735}
736
737void lwan_thread_shutdown(struct lwan *l)
738{
739 lwan_status_debug("Shutting down threads")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 739, __FUNCTION__, "Shutting down threads")
;
740
741 for (unsigned int i = 0; i < l->thread.count; i++) {
742 struct lwan_thread *t = &l->thread.threads[i];
743
744 close(t->epoll_fd);
745 lwan_thread_nudge(t);
746 }
747
748 pthread_barrier_wait(&l->thread.barrier);
749 pthread_barrier_destroy(&l->thread.barrier);
750
751 for (unsigned int i = 0; i < l->thread.count; i++) {
752 struct lwan_thread *t = &l->thread.threads[i];
753
754 close(t->pipe_fd[0]);
755#if !defined(HAVE_EVENTFD)
756 close(t->pipe_fd[1]);
757#endif
758
759 pthread_join(l->thread.threads[i].self, NULL((void*)0));
760 spsc_queue_free(&t->pending_fds);
761 timeouts_close(t->wheel);
762 }
763
764 free(l->thread.threads);
765}