Bug Summary

File:lwan-thread.c
Warning:line 653, 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 -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -fno-plt -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -resource-dir /usr/lib/clang/9.0.1 -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/9.0.1/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 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-output=html -faddrsig -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2020-04-10-051917-3704192-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
280 if (yield_result == CONN_CORO_ASYNC_RESUME) {
281 op = EPOLL_CTL_DEL2;
282 yield_result = CONN_CORO_RESUME;
283 } else {
284 op = EPOLL_CTL_ADD1;
285 yield_result = CONN_CORO_SUSPEND_ASYNC_AWAIT;
286 }
287
288 if (UNLIKELY(epoll_ctl(epoll_fd, op, await_fd, &event) < 0)__builtin_expect(((epoll_ctl(epoll_fd, op, await_fd, &event
) < 0)), (0))
)
289 lwan_status_perror("epoll_ctl")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 289, __FUNCTION__, "epoll_ctl")
;
290 }
291
292 return update_epoll_flags(lwan_connection_get_fd(tq->lwan, conn), conn,
293 epoll_fd, yield_result);
294}
295
296static void update_date_cache(struct lwan_thread *thread)
297{
298 time_t now = time(NULL((void*)0));
299
300 lwan_format_rfc_time(now, thread->date.date);
301 lwan_format_rfc_time(now + (time_t)thread->lwan->config.expires,
302 thread->date.expires);
303}
304
305static ALWAYS_INLINEinline __attribute__((always_inline)) void spawn_coro(struct lwan_connection *conn,
306 struct coro_switcher *switcher,
307 struct timeout_queue *tq)
308{
309 struct lwan_thread *t = conn->thread;
310
311 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"
, 311, __extension__ __PRETTY_FUNCTION__); }))
;
312 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"
, 312, __extension__ __PRETTY_FUNCTION__); }))
;
313 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"
, 313, __extension__ __PRETTY_FUNCTION__); }))
;
314 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"
, 315, __extension__ __PRETTY_FUNCTION__); }))
315 (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"
, 315, __extension__ __PRETTY_FUNCTION__); }))
;
316
317 *conn = (struct lwan_connection) {
318 .coro = coro_new(switcher, process_request_coro, conn),
319 .flags = CONN_EVENTS_READ,
320 .time_to_expire = tq->current_time + tq->move_to_last_bump,
321 .thread = t,
322 };
323 if (UNLIKELY(!conn->coro)__builtin_expect(((!conn->coro)), (0))) {
324 conn->flags = 0;
325 lwan_status_error("Could not create coroutine")lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 325, __FUNCTION__, "Could not create coroutine")
;
326 return;
327 }
328
329 timeout_queue_insert(tq, conn);
330}
331
332static void accept_nudge(int pipe_fd,
333 struct lwan_thread *t,
334 struct lwan_connection *conns,
335 struct timeout_queue *tq,
336 struct coro_switcher *switcher,
337 int epoll_fd)
338{
339 uint64_t event;
340 int new_fd;
341
342 /* Errors are ignored here as pipe_fd serves just as a way to wake the
343 * thread from epoll_wait(). It's fine to consume the queue at this
344 * point, regardless of the error type. */
345 (void)read(pipe_fd, &event, sizeof(event));
346
347 while (spsc_queue_pop(&t->pending_fds, &new_fd)) {
348 struct lwan_connection *conn = &conns[new_fd];
349 struct epoll_event ev = {
350 .data.ptr = conn,
351 .events = conn_flags_to_epoll_events(CONN_EVENTS_READ),
352 };
353
354 if (LIKELY(!epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_fd, &ev))__builtin_expect((!!(!epoll_ctl(epoll_fd, 1, new_fd, &ev)
)), (1))
)
355 spawn_coro(conn, switcher, tq);
356 }
357
358 timeouts_add(t->wheel, &tq->timeout, 1000);
359}
360
361static bool_Bool process_pending_timers(struct timeout_queue *tq,
362 struct lwan_thread *t,
363 int epoll_fd)
364{
365 struct timeout *timeout;
366 bool_Bool should_expire_timers = false0;
367
368 while ((timeout = timeouts_get(t->wheel))) {
369 struct lwan_request *request;
370
371 if (timeout == &tq->timeout) {
372 should_expire_timers = true1;
373 continue;
374 }
375
376 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))
;
377
378 update_epoll_flags(request->fd, request->conn, epoll_fd,
379 CONN_CORO_RESUME);
380 }
381
382 if (should_expire_timers) {
383 timeout_queue_expire_waiting(tq);
384
385 /* tq timeout expires every 1000ms if there are connections, so
386 * update the date cache at this point as well. */
387 update_date_cache(t);
388
389 if (!timeout_queue_empty(tq)) {
390 timeouts_add(t->wheel, &tq->timeout, 1000);
391 return true1;
392 }
393
394 timeouts_del(t->wheel, &tq->timeout);
395 }
396
397 return false0;
398}
399
400static int
401turn_timer_wheel(struct timeout_queue *tq, struct lwan_thread *t, int epoll_fd)
402{
403 timeout_t wheel_timeout;
404 struct timespec now;
405
406 if (UNLIKELY(clock_gettime(monotonic_clock_id, &now) < 0)__builtin_expect(((clock_gettime(monotonic_clock_id, &now
) < 0)), (0))
)
407 lwan_status_critical("Could not get monotonic time")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 407, __FUNCTION__, "Could not get monotonic time")
;
408
409 timeouts_update(t->wheel,
410 (timeout_t)(now.tv_sec * 1000 + now.tv_nsec / 1000000));
411
412 wheel_timeout = timeouts_timeout(t->wheel);
413 if (UNLIKELY((int64_t)wheel_timeout < 0)__builtin_expect((((int64_t)wheel_timeout < 0)), (0)))
414 goto infinite_timeout;
415
416 if (wheel_timeout == 0) {
417 if (!process_pending_timers(tq, t, epoll_fd))
418 goto infinite_timeout;
419
420 wheel_timeout = timeouts_timeout(t->wheel);
421 if (wheel_timeout == 0)
422 goto infinite_timeout;
423 }
424
425 return (int)wheel_timeout;
426
427infinite_timeout:
428 return -1;
429}
430
431static void *thread_io_loop(void *data)
432{
433 struct lwan_thread *t = data;
434 int epoll_fd = t->epoll_fd;
435 const int read_pipe_fd = t->pipe_fd[0];
436 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; })
;
437 struct lwan *lwan = t->lwan;
438 struct epoll_event *events;
439 struct coro_switcher switcher;
440 struct timeout_queue tq;
441
442 lwan_status_debug("Worker thread #%zd starting",lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 443, __FUNCTION__, "Worker thread #%zd starting", t - t->
lwan->thread.threads + 1)
443 t - t->lwan->thread.threads + 1)lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 443, __FUNCTION__, "Worker thread #%zd starting", t - t->
lwan->thread.threads + 1)
;
444 lwan_set_thread_name("worker");
445
446 events = calloc((size_t)max_events, sizeof(*events));
447 if (UNLIKELY(!events)__builtin_expect(((!events)), (0)))
448 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"
, 448, __FUNCTION__, "Could not allocate memory for events")
;
449
450 update_date_cache(t);
451
452 timeout_queue_init(&tq, lwan);
453
454 pthread_barrier_wait(&lwan->thread.barrier);
455
456 for (;;) {
457 int timeout = turn_timer_wheel(&tq, t, epoll_fd);
458 int n_fds = epoll_wait(epoll_fd, events, max_events, timeout);
459
460 if (UNLIKELY(n_fds < 0)__builtin_expect(((n_fds < 0)), (0))) {
461 if (errno(*__errno_location ()) == EBADF9 || errno(*__errno_location ()) == EINVAL22)
462 break;
463 continue;
464 }
465
466 for (struct epoll_event *event = events; n_fds--; event++) {
467 struct lwan_connection *conn;
468
469 if (UNLIKELY(!event->data.ptr)__builtin_expect(((!event->data.ptr)), (0))) {
470 accept_nudge(read_pipe_fd, t, lwan->conns, &tq, &switcher,
471 epoll_fd);
472 continue;
473 }
474
475 conn = event->data.ptr;
476
477 if (UNLIKELY(event->events & (EPOLLRDHUP | EPOLLHUP))__builtin_expect(((event->events & (EPOLLRDHUP | EPOLLHUP
))), (0))
) {
478 timeout_queue_expire(&tq, conn);
479 continue;
480 }
481
482 resume_coro(&tq, conn, epoll_fd);
483 timeout_queue_move_to_last(&tq, conn);
484 }
485 }
486
487 pthread_barrier_wait(&lwan->thread.barrier);
488
489 timeout_queue_expire_all(&tq);
490 free(events);
491
492 return NULL((void*)0);
493}
494
495static void create_thread(struct lwan *l, struct lwan_thread *thread,
496 const size_t n_queue_fds)
497{
498 int ignore;
499 pthread_attr_t attr;
500
501 memset(thread, 0, sizeof(*thread));
502 thread->lwan = l;
503
504 thread->wheel = timeouts_open(&ignore);
505 if (!thread->wheel)
506 lwan_status_critical("Could not create timer wheel")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 506, __FUNCTION__, "Could not create timer wheel")
;
507
508 if ((thread->epoll_fd = epoll_create1(EPOLL_CLOEXECEPOLL_CLOEXEC)) < 0)
509 lwan_status_critical_perror("epoll_create")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 509, __FUNCTION__, "epoll_create")
;
510
511 if (pthread_attr_init(&attr))
512 lwan_status_critical_perror("pthread_attr_init")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 512, __FUNCTION__, "pthread_attr_init")
;
513
514 if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEMPTHREAD_SCOPE_SYSTEM))
515 lwan_status_critical_perror("pthread_attr_setscope")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 515, __FUNCTION__, "pthread_attr_setscope")
;
516
517 if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLEPTHREAD_CREATE_JOINABLE))
518 lwan_status_critical_perror("pthread_attr_setdetachstate")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 518, __FUNCTION__, "pthread_attr_setdetachstate")
;
519
520#if defined(HAVE_EVENTFD)
521 int efd = eventfd(0, EFD_NONBLOCKEFD_NONBLOCK | EFD_SEMAPHOREEFD_SEMAPHORE | EFD_CLOEXECEFD_CLOEXEC);
522 if (efd < 0)
523 lwan_status_critical_perror("eventfd")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 523, __FUNCTION__, "eventfd")
;
524
525 thread->pipe_fd[0] = thread->pipe_fd[1] = efd;
526#else
527 if (pipe2(thread->pipe_fd, O_NONBLOCK04000 | O_CLOEXEC02000000) < 0)
528 lwan_status_critical_perror("pipe")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 528, __FUNCTION__, "pipe")
;
529#endif
530
531 struct epoll_event event = { .events = EPOLLINEPOLLIN, .data.ptr = NULL((void*)0) };
532 if (epoll_ctl(thread->epoll_fd, EPOLL_CTL_ADD1, thread->pipe_fd[0], &event) < 0)
533 lwan_status_critical_perror("epoll_ctl")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 533, __FUNCTION__, "epoll_ctl")
;
534
535 if (pthread_create(&thread->self, &attr, thread_io_loop, thread))
536 lwan_status_critical_perror("pthread_create")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 536, __FUNCTION__, "pthread_create")
;
537
538 if (pthread_attr_destroy(&attr))
539 lwan_status_critical_perror("pthread_attr_destroy")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 539, __FUNCTION__, "pthread_attr_destroy")
;
540
541 if (spsc_queue_init(&thread->pending_fds, n_queue_fds) < 0) {
542 lwan_status_critical("Could not initialize pending fd "lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 543, __FUNCTION__, "Could not initialize pending fd " "queue width %zu elements"
, n_queue_fds)
543 "queue width %zu elements", n_queue_fds)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 543, __FUNCTION__, "Could not initialize pending fd " "queue width %zu elements"
, n_queue_fds)
;
544 }
545}
546
547void lwan_thread_nudge(struct lwan_thread *t)
548{
549 uint64_t event = 1;
550
551 if (UNLIKELY(write(t->pipe_fd[1], &event, sizeof(event)) < 0)__builtin_expect(((write(t->pipe_fd[1], &event, sizeof
(event)) < 0)), (0))
)
552 lwan_status_perror("write")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 552, __FUNCTION__, "write")
;
553}
554
555void lwan_thread_add_client(struct lwan_thread *t, int fd)
556{
557 for (int i = 0; i < 10; i++) {
558 bool_Bool pushed = spsc_queue_push(&t->pending_fds, fd);
559
560 if (LIKELY(pushed)__builtin_expect((!!(pushed)), (1)))
561 return;
562
563 /* Queue is full; nudge the thread to consume it. */
564 lwan_thread_nudge(t);
565 }
566
567 lwan_status_error("Dropping connection %d", fd)lwan_status_error_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 567, __FUNCTION__, "Dropping connection %d", fd)
;
568 /* FIXME: send "busy" response now, even without receiving request? */
569 close(fd);
570}
571
572#if defined(__linux__1) && defined(__x86_64__1)
573static bool_Bool read_cpu_topology(struct lwan *l, uint32_t siblings[])
574{
575 char path[PATH_MAX4096];
576
577 for (unsigned int i = 0; i < l->n_cpus; i++) {
578 FILE *sib;
579 uint32_t id, sibling;
580 char separator;
581
582 snprintf(path, sizeof(path),
583 "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list",
584 i);
585
586 sib = fopen(path, "re");
587 if (!sib) {
588 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"
, 589, __FUNCTION__, "Could not open `%s` to determine CPU topology"
, path)
589 path)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 589, __FUNCTION__, "Could not open `%s` to determine CPU topology"
, path)
;
590 return false0;
591 }
592
593 switch (fscanf(sib, "%u%c%u", &id, &separator, &sibling)) {
594 case 2: /* No SMT */
595 siblings[i] = id;
596 break;
597 case 3: /* SMT */
598 if (!(separator == ',' || separator == '-')) {
599 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"
, 599, __FUNCTION__, "Expecting either ',' or '-' for sibling separator"
)
;
600 __builtin_unreachable();
601 }
602
603 siblings[i] = sibling;
604 break;
605 default:
606 lwan_status_critical("%s has invalid format", path)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 606, __FUNCTION__, "%s has invalid format", path)
;
607 __builtin_unreachable();
608 }
609
610
611 fclose(sib);
612 }
613
614 return true1;
615}
616
617static void
618siblings_to_schedtbl(struct lwan *l, uint32_t siblings[], uint32_t schedtbl[])
619{
620 int *seen = alloca(l->n_cpus * sizeof(int))__builtin_alloca (l->n_cpus * sizeof(int));
621 int n_schedtbl = 0;
622
623 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 626
624 seen[i] = -1;
625
626 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 635
627 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
628 seen[siblings[i]] = (int)i;
629 } else {
630 schedtbl[n_schedtbl++] = (uint32_t)seen[siblings[i]];
631 schedtbl[n_schedtbl++] = i;
632 }
633 }
634
635 if (!
31.1
'n_schedtbl' is 2
n_schedtbl)
32
Taking false branch
636 memcpy(schedtbl, seen, l->n_cpus * sizeof(int));
637}
638
639static void
640topology_to_schedtbl(struct lwan *l, uint32_t schedtbl[], uint32_t n_threads)
641{
642 uint32_t *siblings = alloca(l->n_cpus * sizeof(uint32_t))__builtin_alloca (l->n_cpus * sizeof(uint32_t));
643
644 if (!read_cpu_topology(l, siblings)) {
11
Assuming the condition is false
12
Taking false branch
645 for (uint32_t i = 0; i < n_threads; i++)
646 schedtbl[i] = (i / 2) % l->thread.count;
647 } else {
648 uint32_t *affinity = alloca(l->n_cpus * sizeof(uint32_t))__builtin_alloca (l->n_cpus * sizeof(uint32_t));
649
650 siblings_to_schedtbl(l, siblings, affinity);
13
Calling 'siblings_to_schedtbl'
33
Returning from 'siblings_to_schedtbl'
651
652 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
653 schedtbl[i] = affinity[i % l->n_cpus];
40
Assigned value is garbage or undefined
654 }
655}
656
657static void
658adjust_threads_affinity(struct lwan *l, uint32_t *schedtbl, uint32_t mask)
659{
660 for (uint32_t i = 0; i < l->thread.count; i++) {
661 cpu_set_t set;
662
663 CPU_ZERO(&set)do __builtin_memset (&set, '\0', sizeof (cpu_set_t)); while
(0)
;
664 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; }))
;
665
666 if (pthread_setaffinity_np(l->thread.threads[i].self, sizeof(set),
667 &set))
668 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"
, 668, __FUNCTION__, "Could not set affinity for thread %d", i
)
;
669 }
670}
671#elif defined(__x86_64__1)
672static void
673topology_to_schedtbl(struct lwan *l, uint32_t schedtbl[], uint32_t n_threads)
674{
675 for (uint32_t i = 0; i < n_threads; i++)
676 schedtbl[i] = (i / 2) % l->thread.count;
677}
678
679static void
680adjust_threads_affinity(struct lwan *l, uint32_t *schedtbl, uint32_t n)
681{
682}
683#endif
684
685void lwan_thread_init(struct lwan *l)
686{
687 if (pthread_barrier_init(&l->thread.barrier, NULL((void*)0),
1
Assuming the condition is false
2
Taking false branch
688 (unsigned)l->thread.count + 1))
689 lwan_status_critical("Could not create barrier")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 689, __FUNCTION__, "Could not create barrier")
;
690
691 lwan_status_debug("Initializing threads")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 691, __FUNCTION__, "Initializing threads")
;
692
693 l->thread.threads =
694 calloc((size_t)l->thread.count, sizeof(struct lwan_thread));
695 if (!l->thread.threads)
3
Assuming field 'threads' is non-null
4
Taking false branch
696 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"
, 696, __FUNCTION__, "Could not allocate memory for threads")
;
697
698 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
699 (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
; })
;
700 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"
, 700, __FUNCTION__, "Pending client file descriptor queue has %zu items"
, n_queue_fds)
;
701 for (unsigned int i = 0; i < l->thread.
6.1
'i' is < field 'count'
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 704
702 create_thread(l, &l->thread.threads[i], n_queue_fds);
703
704 const unsigned int total_conns = l->thread.max_fd * l->thread.count;
705#ifdef __x86_64__1
706 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; })]
707 "Two connections per cache line")extern int (*__Static_assert_function (void)) [!!sizeof (struct
{ int __error_if_negative: (sizeof(struct lwan_connection) ==
32) ? 2 : -1; })]
;
708 /*
709 * Pre-schedule each file descriptor, to reduce some operations in the
710 * fast path.
711 *
712 * Since struct lwan_connection is guaranteed to be 32-byte long, two of
713 * them can fill up a cache line. Assume siblings share cache lines and
714 * use the CPU topology to group two connections per cache line in such
715 * a way that false sharing is avoided.
716 */
717 uint32_t n_threads = (uint32_t)lwan_nextpow2((size_t)((l->thread.count - 1) * 2));
718 uint32_t *schedtbl = alloca(n_threads * sizeof(uint32_t))__builtin_alloca (n_threads * sizeof(uint32_t));
719
720 topology_to_schedtbl(l, schedtbl, n_threads);
10
Calling 'topology_to_schedtbl'
721
722 n_threads--; /* Transform count into mask for AND below */
723 adjust_threads_affinity(l, schedtbl, n_threads);
724 for (unsigned int i = 0; i < total_conns; i++)
725 l->conns[i].thread = &l->thread.threads[schedtbl[i & n_threads]];
726#else
727 for (unsigned int i = 0; i < total_conns; i++)
728 l->conns[i].thread = &l->thread.threads[i % l->thread.count];
729#endif
730
731 pthread_barrier_wait(&l->thread.barrier);
732
733 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"
, 733, __FUNCTION__, "Worker threads created and ready to serve"
)
;
734}
735
736void lwan_thread_shutdown(struct lwan *l)
737{
738 lwan_status_debug("Shutting down threads")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan-thread.c"
, 738, __FUNCTION__, "Shutting down threads")
;
739
740 for (unsigned int i = 0; i < l->thread.count; i++) {
741 struct lwan_thread *t = &l->thread.threads[i];
742
743 close(t->epoll_fd);
744 lwan_thread_nudge(t);
745 }
746
747 pthread_barrier_wait(&l->thread.barrier);
748 pthread_barrier_destroy(&l->thread.barrier);
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->pipe_fd[0]);
754#if !defined(HAVE_EVENTFD)
755 close(t->pipe_fd[1]);
756#endif
757
758 pthread_join(l->thread.threads[i].self, NULL((void*)0));
759 spsc_queue_free(&t->pending_fds);
760 timeouts_close(t->wheel);
761 }
762
763 free(l->thread.threads);
764}