Bug Summary

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