Bug Summary

File:lwan-thread.c
Warning:line 663, 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-06-01-152314-338235-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,
169 [CONN_EVENTS_ASYNC_WRITE] = EPOLLETEPOLLET | EPOLLOUTEPOLLOUT | EPOLLRDHUPEPOLLRDHUP,
170 [CONN_EVENTS_ASYNC_READ_WRITE] = EPOLLETEPOLLET | EPOLLINEPOLLIN | EPOLLOUTEPOLLOUT | EPOLLRDHUPEPOLLRDHUP,
171 };
172
173 return map[flags & CONN_EVENTS_MASK];
174}
175
176static void update_epoll_flags(int fd,
177 struct lwan_connection *conn,
178 int epoll_fd,
179 enum lwan_connection_coro_yield yield_result)
180{
181 static const enum lwan_connection_flags or_mask[CONN_CORO_MAX] = {
182 [CONN_CORO_YIELD] = 0,
183
184 [CONN_CORO_WANT_READ_WRITE] = CONN_EVENTS_READ_WRITE,
185 [CONN_CORO_WANT_READ] = CONN_EVENTS_READ,
186 [CONN_CORO_WANT_WRITE] = CONN_EVENTS_WRITE,
187
188 /* While the coro is suspended, we're not interested in either EPOLLIN
189 * or EPOLLOUT events. We still want to track this fd in epoll, though,
190 * so unset both so that only EPOLLRDHUP (plus the implicitly-set ones)
191 * are set. */
192 [CONN_CORO_SUSPEND_TIMER] = CONN_SUSPENDED_TIMER,
193 [CONN_CORO_SUSPEND_ASYNC_AWAIT] = CONN_SUSPENDED_ASYNC_AWAIT,
194
195 /* Ideally, when suspending a coroutine, the current flags&CONN_EVENTS_MASK
196 * would have to be stored and restored -- however, resuming as if the
197 * client coroutine is interested in a write event always guarantees that
198 * they'll be resumed as they're TCP sockets. There's a good chance that
199 * trying to read from a socket after resuming a coroutine will succeed,
200 * but if it doesn't because read() returns -EAGAIN, the I/O wrappers will
201 * yield with CONN_CORO_WANT_READ anyway. */
202 [CONN_CORO_RESUME] = CONN_EVENTS_WRITE,
203 };
204 static const enum lwan_connection_flags and_mask[CONN_CORO_MAX] = {
205 [CONN_CORO_YIELD] = ~0,
206
207 [CONN_CORO_WANT_READ_WRITE] = ~0,
208 [CONN_CORO_WANT_READ] = ~CONN_EVENTS_WRITE,
209 [CONN_CORO_WANT_WRITE] = ~CONN_EVENTS_READ,
210
211 [CONN_CORO_SUSPEND_TIMER] = ~(CONN_EVENTS_READ_WRITE | CONN_SUSPENDED_ASYNC_AWAIT),
212 [CONN_CORO_SUSPEND_ASYNC_AWAIT] = ~(CONN_EVENTS_READ_WRITE | CONN_SUSPENDED_TIMER),
213 [CONN_CORO_RESUME] = ~CONN_SUSPENDED,
214 };
215 enum lwan_connection_flags prev_flags = conn->flags;
216
217 conn->flags |= or_mask[yield_result];
218 conn->flags &= and_mask[yield_result];
219
220 if (conn->flags == prev_flags)
221 return;
222
223 struct epoll_event event = {
224 .events = conn_flags_to_epoll_events(conn->flags),
225 .data.ptr = conn,
226 };
227
228 if (UNLIKELY(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0)__builtin_expect(((epoll_ctl(epoll_fd, 3, fd, &event) <
0)), (0))
)
229 lwan_status_perror("epoll_ctl")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 229, __FUNCTION__, "epoll_ctl")
;
230}
231
232static void clear_async_await_flag(void *data)
233{
234 struct lwan_connection *async_fd_conn = data;
235
236 async_fd_conn->flags &= ~CONN_ASYNC_AWAIT;
237}
238
239static enum lwan_connection_coro_yield
240resume_async(struct timeout_queue *tq,
241 enum lwan_connection_coro_yield yield_result,
242 int64_t from_coro,
243 struct lwan_connection *conn,
244 int epoll_fd)
245{
246 static const enum lwan_connection_flags to_connection_flags[] = {
247 [CONN_CORO_ASYNC_AWAIT_READ] = CONN_EVENTS_ASYNC_READ,
248 [CONN_CORO_ASYNC_AWAIT_WRITE] = CONN_EVENTS_ASYNC_WRITE,
249 [CONN_CORO_ASYNC_AWAIT_READ_WRITE] = CONN_EVENTS_ASYNC_READ_WRITE,
250 };
251 int await_fd = (int)((uint64_t)from_coro >> 32);
252 enum lwan_connection_flags flags;
253 uint32_t events;
254 int op;
255
256 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"
, 256, __extension__ __PRETTY_FUNCTION__); }))
;
257 assert(yield_result >= CONN_CORO_ASYNC_AWAIT_READ &&((void) sizeof ((yield_result >= CONN_CORO_ASYNC_AWAIT_READ
&& yield_result <= CONN_CORO_ASYNC_AWAIT_READ_WRITE
) ? 1 : 0), __extension__ ({ if (yield_result >= CONN_CORO_ASYNC_AWAIT_READ
&& yield_result <= CONN_CORO_ASYNC_AWAIT_READ_WRITE
) ; else __assert_fail ("yield_result >= CONN_CORO_ASYNC_AWAIT_READ && yield_result <= CONN_CORO_ASYNC_AWAIT_READ_WRITE"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 258, __extension__ __PRETTY_FUNCTION__); }))
258 yield_result <= CONN_CORO_ASYNC_AWAIT_READ_WRITE)((void) sizeof ((yield_result >= CONN_CORO_ASYNC_AWAIT_READ
&& yield_result <= CONN_CORO_ASYNC_AWAIT_READ_WRITE
) ? 1 : 0), __extension__ ({ if (yield_result >= CONN_CORO_ASYNC_AWAIT_READ
&& yield_result <= CONN_CORO_ASYNC_AWAIT_READ_WRITE
) ; else __assert_fail ("yield_result >= CONN_CORO_ASYNC_AWAIT_READ && yield_result <= CONN_CORO_ASYNC_AWAIT_READ_WRITE"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 258, __extension__ __PRETTY_FUNCTION__); }))
;
259
260 flags = to_connection_flags[yield_result];
261 events = conn_flags_to_epoll_events(flags);
262
263 assert(events != 0)((void) sizeof ((events != 0) ? 1 : 0), __extension__ ({ if (
events != 0) ; else __assert_fail ("events != 0", "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 263, __extension__ __PRETTY_FUNCTION__); }))
;
264
265 struct lwan_connection *await_fd_conn = &tq->lwan->conns[await_fd];
266 if (LIKELY(await_fd_conn->flags & CONN_ASYNC_AWAIT)__builtin_expect((!!(await_fd_conn->flags & CONN_ASYNC_AWAIT
)), (1))
) {
267 if (LIKELY((await_fd_conn->flags & CONN_EVENTS_MASK) == flags)__builtin_expect((!!((await_fd_conn->flags & CONN_EVENTS_MASK
) == flags)), (1))
)
268 return CONN_CORO_SUSPEND_ASYNC_AWAIT;
269
270 op = EPOLL_CTL_MOD3;
271 } else {
272 op = EPOLL_CTL_ADD1;
273 flags |= CONN_ASYNC_AWAIT;
274 coro_defer(conn->coro, clear_async_await_flag, await_fd_conn);
275 }
276
277 struct epoll_event event = {.events = events, .data.ptr = conn};
278 if (LIKELY(!epoll_ctl(epoll_fd, op, await_fd, &event))__builtin_expect((!!(!epoll_ctl(epoll_fd, op, await_fd, &
event))), (1))
) {
279 await_fd_conn->flags &= ~CONN_EVENTS_MASK;
280 await_fd_conn->flags |= flags;
281 return CONN_CORO_SUSPEND_ASYNC_AWAIT;
282 }
283
284 return CONN_CORO_ABORT;
285}
286
287static ALWAYS_INLINEinline __attribute__((always_inline)) void resume_coro(struct timeout_queue *tq,
288 struct lwan_connection *conn,
289 int epoll_fd)
290{
291 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"
, 291, __extension__ __PRETTY_FUNCTION__); }))
;
292
293 int64_t from_coro = coro_resume(conn->coro);
294 enum lwan_connection_coro_yield yield_result = from_coro & 0xffffffff;
295
296 if (UNLIKELY(yield_result >= CONN_CORO_ASYNC)__builtin_expect(((yield_result >= CONN_CORO_ASYNC)), (0)))
297 yield_result = resume_async(tq, yield_result, from_coro, conn, epoll_fd);
298
299 if (UNLIKELY(yield_result == CONN_CORO_ABORT)__builtin_expect(((yield_result == CONN_CORO_ABORT)), (0)))
300 return timeout_queue_expire(tq, conn);
301
302 return update_epoll_flags(lwan_connection_get_fd(tq->lwan, conn), conn,
303 epoll_fd, yield_result);
304}
305
306static void update_date_cache(struct lwan_thread *thread)
307{
308 time_t now = time(NULL((void*)0));
309
310 lwan_format_rfc_time(now, thread->date.date);
311 lwan_format_rfc_time(now + (time_t)thread->lwan->config.expires,
312 thread->date.expires);
313}
314
315static ALWAYS_INLINEinline __attribute__((always_inline)) void spawn_coro(struct lwan_connection *conn,
316 struct coro_switcher *switcher,
317 struct timeout_queue *tq)
318{
319 struct lwan_thread *t = conn->thread;
320
321 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"
, 321, __extension__ __PRETTY_FUNCTION__); }))
;
322 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"
, 322, __extension__ __PRETTY_FUNCTION__); }))
;
323 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"
, 323, __extension__ __PRETTY_FUNCTION__); }))
;
324 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"
, 325, __extension__ __PRETTY_FUNCTION__); }))
325 (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"
, 325, __extension__ __PRETTY_FUNCTION__); }))
;
326
327 *conn = (struct lwan_connection) {
328 .coro = coro_new(switcher, process_request_coro, conn),
329 .flags = CONN_EVENTS_READ,
330 .time_to_expire = tq->current_time + tq->move_to_last_bump,
331 .thread = t,
332 };
333 if (UNLIKELY(!conn->coro)__builtin_expect(((!conn->coro)), (0))) {
334 conn->flags = 0;
335 lwan_status_error("Could not create coroutine")lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 335, __FUNCTION__, "Could not create coroutine")
;
336 return;
337 }
338
339 timeout_queue_insert(tq, conn);
340}
341
342static void accept_nudge(int pipe_fd,
343 struct lwan_thread *t,
344 struct lwan_connection *conns,
345 struct timeout_queue *tq,
346 struct coro_switcher *switcher,
347 int epoll_fd)
348{
349 uint64_t event;
350 int new_fd;
351
352 /* Errors are ignored here as pipe_fd serves just as a way to wake the
353 * thread from epoll_wait(). It's fine to consume the queue at this
354 * point, regardless of the error type. */
355 (void)read(pipe_fd, &event, sizeof(event));
356
357 while (spsc_queue_pop(&t->pending_fds, &new_fd)) {
358 struct lwan_connection *conn = &conns[new_fd];
359 struct epoll_event ev = {
360 .data.ptr = conn,
361 .events = conn_flags_to_epoll_events(CONN_EVENTS_READ),
362 };
363
364 if (LIKELY(!epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_fd, &ev))__builtin_expect((!!(!epoll_ctl(epoll_fd, 1, new_fd, &ev)
)), (1))
)
365 spawn_coro(conn, switcher, tq);
366 }
367
368 timeouts_add(t->wheel, &tq->timeout, 1000);
369}
370
371static bool_Bool process_pending_timers(struct timeout_queue *tq,
372 struct lwan_thread *t,
373 int epoll_fd)
374{
375 struct timeout *timeout;
376 bool_Bool should_expire_timers = false0;
377
378 while ((timeout = timeouts_get(t->wheel))) {
379 struct lwan_request *request;
380
381 if (timeout == &tq->timeout) {
382 should_expire_timers = true1;
383 continue;
384 }
385
386 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))
;
387
388 update_epoll_flags(request->fd, request->conn, epoll_fd,
389 CONN_CORO_RESUME);
390 }
391
392 if (should_expire_timers) {
393 timeout_queue_expire_waiting(tq);
394
395 /* tq timeout expires every 1000ms if there are connections, so
396 * update the date cache at this point as well. */
397 update_date_cache(t);
398
399 if (!timeout_queue_empty(tq)) {
400 timeouts_add(t->wheel, &tq->timeout, 1000);
401 return true1;
402 }
403
404 timeouts_del(t->wheel, &tq->timeout);
405 }
406
407 return false0;
408}
409
410static int
411turn_timer_wheel(struct timeout_queue *tq, struct lwan_thread *t, int epoll_fd)
412{
413 timeout_t wheel_timeout;
414 struct timespec now;
415
416 if (UNLIKELY(clock_gettime(monotonic_clock_id, &now) < 0)__builtin_expect(((clock_gettime(monotonic_clock_id, &now
) < 0)), (0))
)
417 lwan_status_critical("Could not get monotonic time")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 417, __FUNCTION__, "Could not get monotonic time")
;
418
419 timeouts_update(t->wheel,
420 (timeout_t)(now.tv_sec * 1000 + now.tv_nsec / 1000000));
421
422 wheel_timeout = timeouts_timeout(t->wheel);
423 if (UNLIKELY((int64_t)wheel_timeout < 0)__builtin_expect((((int64_t)wheel_timeout < 0)), (0)))
424 goto infinite_timeout;
425
426 if (wheel_timeout == 0) {
427 if (!process_pending_timers(tq, t, epoll_fd))
428 goto infinite_timeout;
429
430 wheel_timeout = timeouts_timeout(t->wheel);
431 if (wheel_timeout == 0)
432 goto infinite_timeout;
433 }
434
435 return (int)wheel_timeout;
436
437infinite_timeout:
438 return -1;
439}
440
441static void *thread_io_loop(void *data)
442{
443 struct lwan_thread *t = data;
444 int epoll_fd = t->epoll_fd;
445 const int read_pipe_fd = t->pipe_fd[0];
446 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; })
;
447 struct lwan *lwan = t->lwan;
448 struct epoll_event *events;
449 struct coro_switcher switcher;
450 struct timeout_queue tq;
451
452 lwan_status_debug("Worker thread #%zd starting",lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 453, __FUNCTION__, "Worker thread #%zd starting", t - t->
lwan->thread.threads + 1)
453 t - t->lwan->thread.threads + 1)lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 453, __FUNCTION__, "Worker thread #%zd starting", t - t->
lwan->thread.threads + 1)
;
454 lwan_set_thread_name("worker");
455
456 events = calloc((size_t)max_events, sizeof(*events));
457 if (UNLIKELY(!events)__builtin_expect(((!events)), (0)))
458 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"
, 458, __FUNCTION__, "Could not allocate memory for events")
;
459
460 update_date_cache(t);
461
462 timeout_queue_init(&tq, lwan);
463
464 pthread_barrier_wait(&lwan->thread.barrier);
465
466 for (;;) {
467 int timeout = turn_timer_wheel(&tq, t, epoll_fd);
468 int n_fds = epoll_wait(epoll_fd, events, max_events, timeout);
469
470 if (UNLIKELY(n_fds < 0)__builtin_expect(((n_fds < 0)), (0))) {
471 if (errno(*__errno_location ()) == EBADF9 || errno(*__errno_location ()) == EINVAL22)
472 break;
473 continue;
474 }
475
476 for (struct epoll_event *event = events; n_fds--; event++) {
477 struct lwan_connection *conn;
478
479 if (UNLIKELY(!event->data.ptr)__builtin_expect(((!event->data.ptr)), (0))) {
480 accept_nudge(read_pipe_fd, t, lwan->conns, &tq, &switcher,
481 epoll_fd);
482 continue;
483 }
484
485 conn = event->data.ptr;
486
487 if (UNLIKELY(event->events & (EPOLLRDHUP | EPOLLHUP))__builtin_expect(((event->events & (EPOLLRDHUP | EPOLLHUP
))), (0))
) {
488 timeout_queue_expire(&tq, conn);
489 continue;
490 }
491
492 resume_coro(&tq, conn, epoll_fd);
493 timeout_queue_move_to_last(&tq, conn);
494 }
495 }
496
497 pthread_barrier_wait(&lwan->thread.barrier);
498
499 timeout_queue_expire_all(&tq);
500 free(events);
501
502 return NULL((void*)0);
503}
504
505static void create_thread(struct lwan *l, struct lwan_thread *thread,
506 const size_t n_queue_fds)
507{
508 int ignore;
509 pthread_attr_t attr;
510
511 memset(thread, 0, sizeof(*thread));
512 thread->lwan = l;
513
514 thread->wheel = timeouts_open(&ignore);
515 if (!thread->wheel)
516 lwan_status_critical("Could not create timer wheel")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 516, __FUNCTION__, "Could not create timer wheel")
;
517
518 if ((thread->epoll_fd = epoll_create1(EPOLL_CLOEXECEPOLL_CLOEXEC)) < 0)
519 lwan_status_critical_perror("epoll_create")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 519, __FUNCTION__, "epoll_create")
;
520
521 if (pthread_attr_init(&attr))
522 lwan_status_critical_perror("pthread_attr_init")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 522, __FUNCTION__, "pthread_attr_init")
;
523
524 if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEMPTHREAD_SCOPE_SYSTEM))
525 lwan_status_critical_perror("pthread_attr_setscope")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 525, __FUNCTION__, "pthread_attr_setscope")
;
526
527 if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLEPTHREAD_CREATE_JOINABLE))
528 lwan_status_critical_perror("pthread_attr_setdetachstate")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 528, __FUNCTION__, "pthread_attr_setdetachstate")
;
529
530#if defined(HAVE_EVENTFD)
531 int efd = eventfd(0, EFD_NONBLOCKEFD_NONBLOCK | EFD_SEMAPHOREEFD_SEMAPHORE | EFD_CLOEXECEFD_CLOEXEC);
532 if (efd < 0)
533 lwan_status_critical_perror("eventfd")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 533, __FUNCTION__, "eventfd")
;
534
535 thread->pipe_fd[0] = thread->pipe_fd[1] = efd;
536#else
537 if (pipe2(thread->pipe_fd, O_NONBLOCK04000 | O_CLOEXEC02000000) < 0)
538 lwan_status_critical_perror("pipe")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 538, __FUNCTION__, "pipe")
;
539#endif
540
541 struct epoll_event event = { .events = EPOLLINEPOLLIN, .data.ptr = NULL((void*)0) };
542 if (epoll_ctl(thread->epoll_fd, EPOLL_CTL_ADD1, thread->pipe_fd[0], &event) < 0)
543 lwan_status_critical_perror("epoll_ctl")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 543, __FUNCTION__, "epoll_ctl")
;
544
545 if (pthread_create(&thread->self, &attr, thread_io_loop, thread))
546 lwan_status_critical_perror("pthread_create")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 546, __FUNCTION__, "pthread_create")
;
547
548 if (pthread_attr_destroy(&attr))
549 lwan_status_critical_perror("pthread_attr_destroy")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 549, __FUNCTION__, "pthread_attr_destroy")
;
550
551 if (spsc_queue_init(&thread->pending_fds, n_queue_fds) < 0) {
552 lwan_status_critical("Could not initialize pending fd "lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 553, __FUNCTION__, "Could not initialize pending fd " "queue width %zu elements"
, n_queue_fds)
553 "queue width %zu elements", n_queue_fds)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 553, __FUNCTION__, "Could not initialize pending fd " "queue width %zu elements"
, n_queue_fds)
;
554 }
555}
556
557void lwan_thread_nudge(struct lwan_thread *t)
558{
559 uint64_t event = 1;
560
561 if (UNLIKELY(write(t->pipe_fd[1], &event, sizeof(event)) < 0)__builtin_expect(((write(t->pipe_fd[1], &event, sizeof
(event)) < 0)), (0))
)
562 lwan_status_perror("write")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 562, __FUNCTION__, "write")
;
563}
564
565void lwan_thread_add_client(struct lwan_thread *t, int fd)
566{
567 for (int i = 0; i < 10; i++) {
568 bool_Bool pushed = spsc_queue_push(&t->pending_fds, fd);
569
570 if (LIKELY(pushed)__builtin_expect((!!(pushed)), (1)))
571 return;
572
573 /* Queue is full; nudge the thread to consume it. */
574 lwan_thread_nudge(t);
575 }
576
577 lwan_status_error("Dropping connection %d", fd)lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 577, __FUNCTION__, "Dropping connection %d", fd)
;
578 /* FIXME: send "busy" response now, even without receiving request? */
579 close(fd);
580}
581
582#if defined(__linux__1) && defined(__x86_64__1)
583static bool_Bool read_cpu_topology(struct lwan *l, uint32_t siblings[])
584{
585 char path[PATH_MAX4096];
586
587 for (unsigned int i = 0; i < l->n_cpus; i++) {
588 FILE *sib;
589 uint32_t id, sibling;
590 char separator;
591
592 snprintf(path, sizeof(path),
593 "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
594 i);
595
596 sib = fopen(path, "re");
597 if (!sib) {
598 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"
, 599, __FUNCTION__, "Could not open `%s` to determine CPU topology"
, path)
599 path)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 599, __FUNCTION__, "Could not open `%s` to determine CPU topology"
, path)
;
600 return false0;
601 }
602
603 switch (fscanf(sib, "%u%c%u", &id, &separator, &sibling)) {
604 case 2: /* No SMT */
605 siblings[i] = id;
606 break;
607 case 3: /* SMT */
608 if (!(separator == ',' || separator == '-')) {
609 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"
, 609, __FUNCTION__, "Expecting either ',' or '-' for sibling separator"
)
;
610 __builtin_unreachable();
611 }
612
613 siblings[i] = sibling;
614 break;
615 default:
616 lwan_status_critical("%s has invalid format", path)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 616, __FUNCTION__, "%s has invalid format", path)
;
617 __builtin_unreachable();
618 }
619
620
621 fclose(sib);
622 }
623
624 return true1;
625}
626
627static void
628siblings_to_schedtbl(struct lwan *l, uint32_t siblings[], uint32_t schedtbl[])
629{
630 int *seen = alloca(l->n_cpus * sizeof(int))__builtin_alloca (l->n_cpus * sizeof(int));
631 int n_schedtbl = 0;
632
633 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 636
634 seen[i] = -1;
635
636 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 645
637 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
638 seen[siblings[i]] = (int)i;
639 } else {
640 schedtbl[n_schedtbl++] = (uint32_t)seen[siblings[i]];
641 schedtbl[n_schedtbl++] = i;
642 }
643 }
644
645 if (!n_schedtbl
31.1
'n_schedtbl' is 2
)
32
Taking false branch
646 memcpy(schedtbl, seen, l->n_cpus * sizeof(int));
647}
648
649static void
650topology_to_schedtbl(struct lwan *l, uint32_t schedtbl[], uint32_t n_threads)
651{
652 uint32_t *siblings = alloca(l->n_cpus * sizeof(uint32_t))__builtin_alloca (l->n_cpus * sizeof(uint32_t));
653
654 if (!read_cpu_topology(l, siblings)) {
11
Assuming the condition is false
12
Taking false branch
655 for (uint32_t i = 0; i < n_threads; i++)
656 schedtbl[i] = (i / 2) % l->thread.count;
657 } else {
658 uint32_t *affinity = alloca(l->n_cpus * sizeof(uint32_t))__builtin_alloca (l->n_cpus * sizeof(uint32_t));
659
660 siblings_to_schedtbl(l, siblings, affinity);
13
Calling 'siblings_to_schedtbl'
33
Returning from 'siblings_to_schedtbl'
661
662 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
663 schedtbl[i] = affinity[i % l->n_cpus];
40
Assigned value is garbage or undefined
664 }
665}
666
667static void
668adjust_threads_affinity(struct lwan *l, uint32_t *schedtbl, uint32_t mask)
669{
670 for (uint32_t i = 0; i < l->thread.count; i++) {
671 cpu_set_t set;
672
673 CPU_ZERO(&set)do __builtin_memset (&set, '\0', sizeof (cpu_set_t)); while
(0)
;
674 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; }))
;
675
676 if (pthread_setaffinity_np(l->thread.threads[i].self, sizeof(set),
677 &set))
678 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"
, 678, __FUNCTION__, "Could not set affinity for thread %d", i
)
;
679 }
680}
681#elif defined(__x86_64__1)
682static void
683topology_to_schedtbl(struct lwan *l, uint32_t schedtbl[], uint32_t n_threads)
684{
685 for (uint32_t i = 0; i < n_threads; i++)
686 schedtbl[i] = (i / 2) % l->thread.count;
687}
688
689static void
690adjust_threads_affinity(struct lwan *l, uint32_t *schedtbl, uint32_t n)
691{
692}
693#endif
694
695void lwan_thread_init(struct lwan *l)
696{
697 if (pthread_barrier_init(&l->thread.barrier, NULL((void*)0),
1
Assuming the condition is false
2
Taking false branch
698 (unsigned)l->thread.count + 1))
699 lwan_status_critical("Could not create barrier")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 699, __FUNCTION__, "Could not create barrier")
;
700
701 lwan_status_debug("Initializing threads")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 701, __FUNCTION__, "Initializing threads")
;
702
703 l->thread.threads =
704 calloc((size_t)l->thread.count, sizeof(struct lwan_thread));
705 if (!l->thread.threads)
3
Assuming field 'threads' is non-null
4
Taking false branch
706 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"
, 706, __FUNCTION__, "Could not allocate memory for threads")
;
707
708 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
709 (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
; })
;
710 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"
, 710, __FUNCTION__, "Pending client file descriptor queue has %zu items"
, n_queue_fds)
;
711 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 714
712 create_thread(l, &l->thread.threads[i], n_queue_fds);
713
714 const unsigned int total_conns = l->thread.max_fd * l->thread.count;
715#ifdef __x86_64__1
716 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; })]
717 "Two connections per cache line")extern int (*__Static_assert_function (void)) [!!sizeof (struct
{ int __error_if_negative: (sizeof(struct lwan_connection) ==
32) ? 2 : -1; })]
;
718 /*
719 * Pre-schedule each file descriptor, to reduce some operations in the
720 * fast path.
721 *
722 * Since struct lwan_connection is guaranteed to be 32-byte long, two of
723 * them can fill up a cache line. Assume siblings share cache lines and
724 * use the CPU topology to group two connections per cache line in such
725 * a way that false sharing is avoided.
726 */
727 uint32_t n_threads = (uint32_t)lwan_nextpow2((size_t)((l->thread.count - 1) * 2));
728 uint32_t *schedtbl = alloca(n_threads * sizeof(uint32_t))__builtin_alloca (n_threads * sizeof(uint32_t));
729
730 topology_to_schedtbl(l, schedtbl, n_threads);
10
Calling 'topology_to_schedtbl'
731
732 n_threads--; /* Transform count into mask for AND below */
733 adjust_threads_affinity(l, schedtbl, n_threads);
734 for (unsigned int i = 0; i < total_conns; i++)
735 l->conns[i].thread = &l->thread.threads[schedtbl[i & n_threads]];
736#else
737 for (unsigned int i = 0; i < total_conns; i++)
738 l->conns[i].thread = &l->thread.threads[i % l->thread.count];
739#endif
740
741 pthread_barrier_wait(&l->thread.barrier);
742
743 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"
, 743, __FUNCTION__, "Worker threads created and ready to serve"
)
;
744}
745
746void lwan_thread_shutdown(struct lwan *l)
747{
748 lwan_status_debug("Shutting down threads")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 748, __FUNCTION__, "Shutting down threads")
;
749
750 for (unsigned int i = 0; i < l->thread.count; i++) {
751 struct lwan_thread *t = &l->thread.threads[i];
752
753 close(t->epoll_fd);
754 lwan_thread_nudge(t);
755 }
756
757 pthread_barrier_wait(&l->thread.barrier);
758 pthread_barrier_destroy(&l->thread.barrier);
759
760 for (unsigned int i = 0; i < l->thread.count; i++) {
761 struct lwan_thread *t = &l->thread.threads[i];
762
763 close(t->pipe_fd[0]);
764#if !defined(HAVE_EVENTFD)
765 close(t->pipe_fd[1]);
766#endif
767
768 pthread_join(l->thread.threads[i].self, NULL((void*)0));
769 spsc_queue_free(&t->pending_fds);
770 timeouts_close(t->wheel);
771 }
772
773 free(l->thread.threads);
774}