blob: 25ee3ab947cbde3b85ad6b46ff34ef6854d50c71 [file] [log] [blame]
Christopher Faulet1a2b56e2017-10-12 16:09:09 +02001/*
2 * functions about threads.
3 *
4 * Copyright (C) 2017 Christopher Fauet - cfaulet@haproxy.com
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
Willy Tarreau149ab772019-01-26 14:27:06 +010013#define _GNU_SOURCE
Christopher Faulet339fff82017-10-19 11:59:15 +020014#include <unistd.h>
Willy Tarreau0ccd3222018-07-30 10:34:35 +020015#include <stdlib.h>
Christopher Faulet339fff82017-10-19 11:59:15 +020016#include <fcntl.h>
17
Willy Tarreau149ab772019-01-26 14:27:06 +010018#ifdef USE_CPU_AFFINITY
Willy Tarreaud10385a2021-10-06 22:22:40 +020019# include <sched.h>
20# if defined(__FreeBSD__) || defined(__DragonFly__)
21# include <sys/param.h>
22# ifdef __FreeBSD__
23# include <sys/cpuset.h>
24# endif
25# include <pthread_np.h>
26# endif
27# ifdef __APPLE__
28# include <mach/mach_types.h>
29# include <mach/thread_act.h>
30# include <mach/thread_policy.h>
31# endif
32# include <haproxy/cpuset.h>
Willy Tarreau149ab772019-01-26 14:27:06 +010033#endif
34
Willy Tarreau6be78492020-06-05 00:00:29 +020035#include <haproxy/cfgparse.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020036#include <haproxy/fd.h>
37#include <haproxy/global.h>
Willy Tarreau11bd6f72021-05-08 20:33:02 +020038#include <haproxy/log.h>
Willy Tarreau3f567e42020-05-28 15:29:19 +020039#include <haproxy/thread.h>
Willy Tarreaudced3eb2021-10-05 18:48:23 +020040#include <haproxy/time.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020041#include <haproxy/tools.h>
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020042
David Carliera92c5ce2019-09-13 05:03:12 +010043struct thread_info ha_thread_info[MAX_THREADS] = { };
44THREAD_LOCAL struct thread_info *ti = &ha_thread_info[0];
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020045
46#ifdef USE_THREAD
47
Willy Tarreau56c3b8b2021-04-10 17:28:18 +020048volatile unsigned long threads_want_rdv_mask __read_mostly = 0;
Willy Tarreau60b639c2018-08-02 10:16:17 +020049volatile unsigned long threads_harmless_mask = 0;
Willy Tarreau88d1c5d2021-08-04 11:44:17 +020050volatile unsigned long threads_idle_mask = 0;
Willy Tarreau9a1f5732019-06-09 12:20:02 +020051volatile unsigned long threads_sync_mask = 0;
Willy Tarreau56c3b8b2021-04-10 17:28:18 +020052volatile unsigned long all_threads_mask __read_mostly = 1; // nbthread 1 assumed by default
Willy Tarreau0c026f42018-08-01 19:12:20 +020053THREAD_LOCAL unsigned int tid = 0;
54THREAD_LOCAL unsigned long tid_bit = (1UL << 0);
Willy Tarreau149ab772019-01-26 14:27:06 +010055int thread_cpus_enabled_at_boot = 1;
Willy Tarreau0c026f42018-08-01 19:12:20 +020056
Willy Tarreau60b639c2018-08-02 10:16:17 +020057/* Marks the thread as harmless until the last thread using the rendez-vous
Christopher Fauleta9a9e9a2021-03-25 14:11:36 +010058 * point quits, excluding the current one. Thus an isolated thread may be safely
59 * marked as harmless. Given that we can wait for a long time, sched_yield() is
60 * used when available to offer the CPU resources to competing threads if
61 * needed.
Willy Tarreau60b639c2018-08-02 10:16:17 +020062 */
63void thread_harmless_till_end()
64{
Willy Tarreau286363b2021-08-04 10:33:57 +020065 _HA_ATOMIC_OR(&threads_harmless_mask, tid_bit);
66 while (threads_want_rdv_mask & all_threads_mask & ~tid_bit) {
67 ha_thread_relax();
68 }
Willy Tarreau60b639c2018-08-02 10:16:17 +020069}
70
71/* Isolates the current thread : request the ability to work while all other
Willy Tarreauf519cfa2021-08-04 11:22:07 +020072 * threads are harmless, as defined by thread_harmless_now() (i.e. they're not
73 * going to touch any visible memory area). Only returns once all of them are
74 * harmless, with the current thread's bit in threads_harmless_mask cleared.
75 * Needs to be completed using thread_release().
Willy Tarreau60b639c2018-08-02 10:16:17 +020076 */
77void thread_isolate()
78{
79 unsigned long old;
80
Olivier Houchardb23a61f2019-03-08 18:51:17 +010081 _HA_ATOMIC_OR(&threads_harmless_mask, tid_bit);
82 __ha_barrier_atomic_store();
83 _HA_ATOMIC_OR(&threads_want_rdv_mask, tid_bit);
Willy Tarreau60b639c2018-08-02 10:16:17 +020084
85 /* wait for all threads to become harmless */
86 old = threads_harmless_mask;
87 while (1) {
88 if (unlikely((old & all_threads_mask) != all_threads_mask))
89 old = threads_harmless_mask;
Olivier Houchardb23a61f2019-03-08 18:51:17 +010090 else if (_HA_ATOMIC_CAS(&threads_harmless_mask, &old, old & ~tid_bit))
Willy Tarreau60b639c2018-08-02 10:16:17 +020091 break;
92
Willy Tarreau38171da2019-05-17 16:33:13 +020093 ha_thread_relax();
Willy Tarreau60b639c2018-08-02 10:16:17 +020094 }
95 /* one thread gets released at a time here, with its harmess bit off.
96 * The loss of this bit makes the other one continue to spin while the
97 * thread is working alone.
98 */
99}
100
Willy Tarreau88d1c5d2021-08-04 11:44:17 +0200101/* Isolates the current thread : request the ability to work while all other
102 * threads are idle, as defined by thread_idle_now(). It only returns once
103 * all of them are both harmless and idle, with the current thread's bit in
104 * threads_harmless_mask and idle_mask cleared. Needs to be completed using
105 * thread_release(). By doing so the thread also engages in being safe against
106 * any actions that other threads might be about to start under the same
107 * conditions. This specifically targets destruction of any internal structure,
108 * which implies that the current thread may not hold references to any object.
109 *
110 * Note that a concurrent thread_isolate() will usually win against
111 * thread_isolate_full() as it doesn't consider the idle_mask, allowing it to
112 * get back to the poller or any other fully idle location, that will
113 * ultimately release this one.
114 */
115void thread_isolate_full()
116{
117 unsigned long old;
118
119 _HA_ATOMIC_OR(&threads_idle_mask, tid_bit);
120 _HA_ATOMIC_OR(&threads_harmless_mask, tid_bit);
121 __ha_barrier_atomic_store();
122 _HA_ATOMIC_OR(&threads_want_rdv_mask, tid_bit);
123
124 /* wait for all threads to become harmless */
125 old = threads_harmless_mask;
126 while (1) {
127 unsigned long idle = _HA_ATOMIC_LOAD(&threads_idle_mask);
128
129 if (unlikely((old & all_threads_mask) != all_threads_mask))
130 old = _HA_ATOMIC_LOAD(&threads_harmless_mask);
131 else if ((idle & all_threads_mask) == all_threads_mask &&
132 _HA_ATOMIC_CAS(&threads_harmless_mask, &old, old & ~tid_bit))
133 break;
134
135 ha_thread_relax();
136 }
137
138 /* we're not idle anymore at this point. Other threads waiting on this
139 * condition will need to wait until out next pass to the poller, or
140 * our next call to thread_isolate_full().
141 */
142 _HA_ATOMIC_AND(&threads_idle_mask, ~tid_bit);
143}
144
Willy Tarreau60b639c2018-08-02 10:16:17 +0200145/* Cancels the effect of thread_isolate() by releasing the current thread's bit
Willy Tarreauf519cfa2021-08-04 11:22:07 +0200146 * in threads_want_rdv_mask. This immediately allows other threads to expect be
147 * executed, though they will first have to wait for this thread to become
148 * harmless again (possibly by reaching the poller again).
Willy Tarreau60b639c2018-08-02 10:16:17 +0200149 */
150void thread_release()
151{
Olivier Houchardb23a61f2019-03-08 18:51:17 +0100152 _HA_ATOMIC_AND(&threads_want_rdv_mask, ~tid_bit);
Willy Tarreau60b639c2018-08-02 10:16:17 +0200153}
Christopher Faulet339fff82017-10-19 11:59:15 +0200154
Willy Tarreau9a1f5732019-06-09 12:20:02 +0200155/* Cancels the effect of thread_isolate() by releasing the current thread's bit
156 * in threads_want_rdv_mask and by marking this thread as harmless until the
157 * last worker finishes. The difference with thread_release() is that this one
158 * will not leave the function before others are notified to do the same, so it
159 * guarantees that the current thread will not pass through a subsequent call
160 * to thread_isolate() before others finish.
161 */
162void thread_sync_release()
163{
164 _HA_ATOMIC_OR(&threads_sync_mask, tid_bit);
165 __ha_barrier_atomic_store();
166 _HA_ATOMIC_AND(&threads_want_rdv_mask, ~tid_bit);
167
168 while (threads_want_rdv_mask & all_threads_mask) {
169 _HA_ATOMIC_OR(&threads_harmless_mask, tid_bit);
170 while (threads_want_rdv_mask & all_threads_mask)
171 ha_thread_relax();
172 HA_ATOMIC_AND(&threads_harmless_mask, ~tid_bit);
173 }
174
175 /* the current thread is not harmless anymore, thread_isolate()
176 * is forced to wait till all waiters finish.
177 */
178 _HA_ATOMIC_AND(&threads_sync_mask, ~tid_bit);
179 while (threads_sync_mask & all_threads_mask)
180 ha_thread_relax();
181}
182
Willy Tarreaud10385a2021-10-06 22:22:40 +0200183/* Sets up threads, signals and masks, and starts threads 2 and above.
184 * Does nothing when threads are disabled.
185 */
186void setup_extra_threads(void *(*handler)(void *))
187{
188 sigset_t blocked_sig, old_sig;
189 int i;
190
191 /* ensure the signals will be blocked in every thread */
192 sigfillset(&blocked_sig);
193 sigdelset(&blocked_sig, SIGPROF);
194 sigdelset(&blocked_sig, SIGBUS);
195 sigdelset(&blocked_sig, SIGFPE);
196 sigdelset(&blocked_sig, SIGILL);
197 sigdelset(&blocked_sig, SIGSEGV);
198 pthread_sigmask(SIG_SETMASK, &blocked_sig, &old_sig);
199
200 /* Create nbthread-1 thread. The first thread is the current process */
201 ha_thread_info[0].pthread = pthread_self();
202 for (i = 1; i < global.nbthread; i++)
203 pthread_create(&ha_thread_info[i].pthread, NULL, handler, (void *)(long)i);
204}
205
206/* waits for all threads to terminate. Does nothing when threads are
207 * disabled.
208 */
209void wait_for_threads_completion()
210{
211 int i;
212
213 /* Wait the end of other threads */
214 for (i = 1; i < global.nbthread; i++)
215 pthread_join(ha_thread_info[i].pthread, NULL);
216
217#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
218 show_lock_stats();
219#endif
220}
221
222/* Tries to set the current thread's CPU affinity according to the cpu_map */
223void set_thread_cpu_affinity()
224{
225#if defined(USE_CPU_AFFINITY)
226 /* no affinity setting for the master process */
227 if (master)
228 return;
229
230 /* Now the CPU affinity for all threads */
231 if (ha_cpuset_count(&cpu_map.proc))
232 ha_cpuset_and(&cpu_map.thread[tid], &cpu_map.proc);
233
234 if (ha_cpuset_count(&cpu_map.thread[tid])) {/* only do this if the thread has a THREAD map */
235# if defined(__APPLE__)
236 /* Note: this API is limited to the first 32/64 CPUs */
237 unsigned long set = cpu_map.thread[tid].cpuset;
238 int j;
239
240 while ((j = ffsl(set)) > 0) {
241 thread_affinity_policy_data_t cpu_set = { j - 1 };
242 thread_port_t mthread;
243
244 mthread = pthread_mach_thread_np(ha_thread_info[tid].pthread);
245 thread_policy_set(mthread, THREAD_AFFINITY_POLICY, (thread_policy_t)&cpu_set, 1);
246 set &= ~(1UL << (j - 1));
247 }
248# else
249 struct hap_cpuset *set = &cpu_map.thread[tid];
250
251 pthread_setaffinity_np(ha_thread_info[tid].pthread, sizeof(set->cpuset), &set->cpuset);
252# endif
253 }
254#endif /* USE_CPU_AFFINITY */
255}
256
Willy Tarreau2beaaf72019-05-22 08:43:34 +0200257/* send signal <sig> to thread <thr> */
258void ha_tkill(unsigned int thr, int sig)
259{
David Carliera92c5ce2019-09-13 05:03:12 +0100260 pthread_kill(ha_thread_info[thr].pthread, sig);
Willy Tarreau2beaaf72019-05-22 08:43:34 +0200261}
262
263/* send signal <sig> to all threads. The calling thread is signaled last in
264 * order to allow all threads to synchronize in the handler.
265 */
266void ha_tkillall(int sig)
267{
268 unsigned int thr;
269
270 for (thr = 0; thr < global.nbthread; thr++) {
271 if (!(all_threads_mask & (1UL << thr)))
272 continue;
273 if (thr == tid)
274 continue;
David Carliera92c5ce2019-09-13 05:03:12 +0100275 pthread_kill(ha_thread_info[thr].pthread, sig);
Willy Tarreau2beaaf72019-05-22 08:43:34 +0200276 }
277 raise(sig);
278}
279
Willy Tarreau3d184982020-10-18 10:20:59 +0200280/* these calls are used as callbacks at init time when debugging is on */
Willy Tarreaua8ae77d2018-11-25 19:28:23 +0100281void ha_spin_init(HA_SPINLOCK_T *l)
282{
283 HA_SPIN_INIT(l);
284}
285
Willy Tarreau3d184982020-10-18 10:20:59 +0200286/* these calls are used as callbacks at init time when debugging is on */
Willy Tarreaua8ae77d2018-11-25 19:28:23 +0100287void ha_rwlock_init(HA_RWLOCK_T *l)
288{
289 HA_RWLOCK_INIT(l);
290}
291
Willy Tarreau149ab772019-01-26 14:27:06 +0100292/* returns the number of CPUs the current process is enabled to run on */
293static int thread_cpus_enabled()
294{
295 int ret = 1;
296
297#ifdef USE_CPU_AFFINITY
298#if defined(__linux__) && defined(CPU_COUNT)
299 cpu_set_t mask;
300
301 if (sched_getaffinity(0, sizeof(mask), &mask) == 0)
302 ret = CPU_COUNT(&mask);
Olivier Houchard46453d32019-04-11 00:06:47 +0200303#elif defined(__FreeBSD__) && defined(USE_CPU_AFFINITY)
304 cpuset_t cpuset;
305 if (cpuset_getaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1,
306 sizeof(cpuset), &cpuset) == 0)
307 ret = CPU_COUNT(&cpuset);
David CARLIER6a906012021-01-15 08:09:56 +0000308#elif defined(__APPLE__)
309 ret = (int)sysconf(_SC_NPROCESSORS_ONLN);
Willy Tarreau149ab772019-01-26 14:27:06 +0100310#endif
311#endif
312 ret = MAX(ret, 1);
313 ret = MIN(ret, MAX_THREADS);
314 return ret;
315}
316
Amaury Denoyelle4c9efde2021-03-31 16:57:39 +0200317/* Returns 1 if the cpu set is currently restricted for the process else 0.
318 * Currently only implemented for the Linux platform.
319 */
320int thread_cpu_mask_forced()
321{
322#if defined(__linux__)
323 const int cpus_avail = sysconf(_SC_NPROCESSORS_ONLN);
324 return cpus_avail != thread_cpus_enabled();
325#else
326 return 0;
327#endif
328}
329
Willy Tarreau407ef892021-10-05 18:39:27 +0200330/* Below come the lock-debugging functions */
331
332#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
333
334struct lock_stat lock_stats[LOCK_LABELS];
335
336/* this is only used below */
337static const char *lock_label(enum lock_label label)
338{
339 switch (label) {
340 case TASK_RQ_LOCK: return "TASK_RQ";
341 case TASK_WQ_LOCK: return "TASK_WQ";
342 case LISTENER_LOCK: return "LISTENER";
343 case PROXY_LOCK: return "PROXY";
344 case SERVER_LOCK: return "SERVER";
345 case LBPRM_LOCK: return "LBPRM";
346 case SIGNALS_LOCK: return "SIGNALS";
347 case STK_TABLE_LOCK: return "STK_TABLE";
348 case STK_SESS_LOCK: return "STK_SESS";
349 case APPLETS_LOCK: return "APPLETS";
350 case PEER_LOCK: return "PEER";
351 case SHCTX_LOCK: return "SHCTX";
352 case SSL_LOCK: return "SSL";
353 case SSL_GEN_CERTS_LOCK: return "SSL_GEN_CERTS";
354 case PATREF_LOCK: return "PATREF";
355 case PATEXP_LOCK: return "PATEXP";
356 case VARS_LOCK: return "VARS";
357 case COMP_POOL_LOCK: return "COMP_POOL";
358 case LUA_LOCK: return "LUA";
359 case NOTIF_LOCK: return "NOTIF";
360 case SPOE_APPLET_LOCK: return "SPOE_APPLET";
361 case DNS_LOCK: return "DNS";
362 case PID_LIST_LOCK: return "PID_LIST";
363 case EMAIL_ALERTS_LOCK: return "EMAIL_ALERTS";
364 case PIPES_LOCK: return "PIPES";
365 case TLSKEYS_REF_LOCK: return "TLSKEYS_REF";
366 case AUTH_LOCK: return "AUTH";
367 case LOGSRV_LOCK: return "LOGSRV";
368 case DICT_LOCK: return "DICT";
369 case PROTO_LOCK: return "PROTO";
370 case QUEUE_LOCK: return "QUEUE";
371 case CKCH_LOCK: return "CKCH";
372 case SNI_LOCK: return "SNI";
373 case SSL_SERVER_LOCK: return "SSL_SERVER";
374 case SFT_LOCK: return "SFT";
375 case IDLE_CONNS_LOCK: return "IDLE_CONNS";
376 case QUIC_LOCK: return "QUIC";
377 case OTHER_LOCK: return "OTHER";
378 case DEBUG1_LOCK: return "DEBUG1";
379 case DEBUG2_LOCK: return "DEBUG2";
380 case DEBUG3_LOCK: return "DEBUG3";
381 case DEBUG4_LOCK: return "DEBUG4";
382 case DEBUG5_LOCK: return "DEBUG5";
383 case LOCK_LABELS: break; /* keep compiler happy */
384 };
385 /* only way to come here is consecutive to an internal bug */
386 abort();
387}
388
389void show_lock_stats()
390{
391 int lbl;
392
393 for (lbl = 0; lbl < LOCK_LABELS; lbl++) {
394 if (!lock_stats[lbl].num_write_locked &&
395 !lock_stats[lbl].num_seek_locked &&
396 !lock_stats[lbl].num_read_locked) {
397 fprintf(stderr,
398 "Stats about Lock %s: not used\n",
399 lock_label(lbl));
400 continue;
401 }
402
403 fprintf(stderr,
404 "Stats about Lock %s: \n",
405 lock_label(lbl));
406
407 if (lock_stats[lbl].num_write_locked)
408 fprintf(stderr,
409 "\t # write lock : %lu\n"
410 "\t # write unlock: %lu (%ld)\n"
411 "\t # wait time for write : %.3f msec\n"
412 "\t # wait time for write/lock: %.3f nsec\n",
413 lock_stats[lbl].num_write_locked,
414 lock_stats[lbl].num_write_unlocked,
415 lock_stats[lbl].num_write_unlocked - lock_stats[lbl].num_write_locked,
416 (double)lock_stats[lbl].nsec_wait_for_write / 1000000.0,
417 lock_stats[lbl].num_write_locked ? ((double)lock_stats[lbl].nsec_wait_for_write / (double)lock_stats[lbl].num_write_locked) : 0);
418
419 if (lock_stats[lbl].num_seek_locked)
420 fprintf(stderr,
421 "\t # seek lock : %lu\n"
422 "\t # seek unlock : %lu (%ld)\n"
423 "\t # wait time for seek : %.3f msec\n"
424 "\t # wait time for seek/lock : %.3f nsec\n",
425 lock_stats[lbl].num_seek_locked,
426 lock_stats[lbl].num_seek_unlocked,
427 lock_stats[lbl].num_seek_unlocked - lock_stats[lbl].num_seek_locked,
428 (double)lock_stats[lbl].nsec_wait_for_seek / 1000000.0,
429 lock_stats[lbl].num_seek_locked ? ((double)lock_stats[lbl].nsec_wait_for_seek / (double)lock_stats[lbl].num_seek_locked) : 0);
430
431 if (lock_stats[lbl].num_read_locked)
432 fprintf(stderr,
433 "\t # read lock : %lu\n"
434 "\t # read unlock : %lu (%ld)\n"
435 "\t # wait time for read : %.3f msec\n"
436 "\t # wait time for read/lock : %.3f nsec\n",
437 lock_stats[lbl].num_read_locked,
438 lock_stats[lbl].num_read_unlocked,
439 lock_stats[lbl].num_read_unlocked - lock_stats[lbl].num_read_locked,
440 (double)lock_stats[lbl].nsec_wait_for_read / 1000000.0,
441 lock_stats[lbl].num_read_locked ? ((double)lock_stats[lbl].nsec_wait_for_read / (double)lock_stats[lbl].num_read_locked) : 0);
442 }
443}
444
Willy Tarreau407ef892021-10-05 18:39:27 +0200445void __ha_rwlock_init(struct ha_rwlock *l)
446{
447 memset(l, 0, sizeof(struct ha_rwlock));
448 __RWLOCK_INIT(&l->lock);
449}
450
451void __ha_rwlock_destroy(struct ha_rwlock *l)
452{
453 __RWLOCK_DESTROY(&l->lock);
454 memset(l, 0, sizeof(struct ha_rwlock));
455}
456
457
458void __ha_rwlock_wrlock(enum lock_label lbl, struct ha_rwlock *l,
459 const char *func, const char *file, int line)
460{
461 uint64_t start_time;
462
463 if ((l->info.cur_readers | l->info.cur_seeker | l->info.cur_writer) & tid_bit)
464 abort();
465
466 HA_ATOMIC_OR(&l->info.wait_writers, tid_bit);
467
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200468 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200469 __RWLOCK_WRLOCK(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200470 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_write, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200471
472 HA_ATOMIC_INC(&lock_stats[lbl].num_write_locked);
473
474 l->info.cur_writer = tid_bit;
475 l->info.last_location.function = func;
476 l->info.last_location.file = file;
477 l->info.last_location.line = line;
478
479 HA_ATOMIC_AND(&l->info.wait_writers, ~tid_bit);
480}
481
482int __ha_rwlock_trywrlock(enum lock_label lbl, struct ha_rwlock *l,
483 const char *func, const char *file, int line)
484{
485 uint64_t start_time;
486 int r;
487
488 if ((l->info.cur_readers | l->info.cur_seeker | l->info.cur_writer) & tid_bit)
489 abort();
490
491 /* We set waiting writer because trywrlock could wait for readers to quit */
492 HA_ATOMIC_OR(&l->info.wait_writers, tid_bit);
493
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200494 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200495 r = __RWLOCK_TRYWRLOCK(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200496 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_write, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200497 if (unlikely(r)) {
498 HA_ATOMIC_AND(&l->info.wait_writers, ~tid_bit);
499 return r;
500 }
501 HA_ATOMIC_INC(&lock_stats[lbl].num_write_locked);
502
503 l->info.cur_writer = tid_bit;
504 l->info.last_location.function = func;
505 l->info.last_location.file = file;
506 l->info.last_location.line = line;
507
508 HA_ATOMIC_AND(&l->info.wait_writers, ~tid_bit);
509
510 return 0;
511}
512
513void __ha_rwlock_wrunlock(enum lock_label lbl,struct ha_rwlock *l,
514 const char *func, const char *file, int line)
515{
516 if (unlikely(!(l->info.cur_writer & tid_bit))) {
517 /* the thread is not owning the lock for write */
518 abort();
519 }
520
521 l->info.cur_writer = 0;
522 l->info.last_location.function = func;
523 l->info.last_location.file = file;
524 l->info.last_location.line = line;
525
526 __RWLOCK_WRUNLOCK(&l->lock);
527
528 HA_ATOMIC_INC(&lock_stats[lbl].num_write_unlocked);
529}
530
531void __ha_rwlock_rdlock(enum lock_label lbl,struct ha_rwlock *l)
532{
533 uint64_t start_time;
534
535 if ((l->info.cur_readers | l->info.cur_seeker | l->info.cur_writer) & tid_bit)
536 abort();
537
538 HA_ATOMIC_OR(&l->info.wait_readers, tid_bit);
539
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200540 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200541 __RWLOCK_RDLOCK(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200542 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_read, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200543 HA_ATOMIC_INC(&lock_stats[lbl].num_read_locked);
544
545 HA_ATOMIC_OR(&l->info.cur_readers, tid_bit);
546
547 HA_ATOMIC_AND(&l->info.wait_readers, ~tid_bit);
548}
549
550int __ha_rwlock_tryrdlock(enum lock_label lbl,struct ha_rwlock *l)
551{
552 int r;
553
554 if ((l->info.cur_readers | l->info.cur_seeker | l->info.cur_writer) & tid_bit)
555 abort();
556
557 /* try read should never wait */
558 r = __RWLOCK_TRYRDLOCK(&l->lock);
559 if (unlikely(r))
560 return r;
561 HA_ATOMIC_INC(&lock_stats[lbl].num_read_locked);
562
563 HA_ATOMIC_OR(&l->info.cur_readers, tid_bit);
564
565 return 0;
566}
567
568void __ha_rwlock_rdunlock(enum lock_label lbl,struct ha_rwlock *l)
569{
570 if (unlikely(!(l->info.cur_readers & tid_bit))) {
571 /* the thread is not owning the lock for read */
572 abort();
573 }
574
575 HA_ATOMIC_AND(&l->info.cur_readers, ~tid_bit);
576
577 __RWLOCK_RDUNLOCK(&l->lock);
578
579 HA_ATOMIC_INC(&lock_stats[lbl].num_read_unlocked);
580}
581
582void __ha_rwlock_wrtord(enum lock_label lbl, struct ha_rwlock *l,
583 const char *func, const char *file, int line)
584{
585 uint64_t start_time;
586
587 if ((l->info.cur_readers | l->info.cur_seeker) & tid_bit)
588 abort();
589
590 if (!(l->info.cur_writer & tid_bit))
591 abort();
592
593 HA_ATOMIC_OR(&l->info.wait_readers, tid_bit);
594
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200595 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200596 __RWLOCK_WRTORD(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200597 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_read, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200598
599 HA_ATOMIC_INC(&lock_stats[lbl].num_read_locked);
600
601 HA_ATOMIC_OR(&l->info.cur_readers, tid_bit);
602 HA_ATOMIC_AND(&l->info.cur_writer, ~tid_bit);
603 l->info.last_location.function = func;
604 l->info.last_location.file = file;
605 l->info.last_location.line = line;
606
607 HA_ATOMIC_AND(&l->info.wait_readers, ~tid_bit);
608}
609
610void __ha_rwlock_wrtosk(enum lock_label lbl, struct ha_rwlock *l,
611 const char *func, const char *file, int line)
612{
613 uint64_t start_time;
614
615 if ((l->info.cur_readers | l->info.cur_seeker) & tid_bit)
616 abort();
617
618 if (!(l->info.cur_writer & tid_bit))
619 abort();
620
621 HA_ATOMIC_OR(&l->info.wait_seekers, tid_bit);
622
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200623 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200624 __RWLOCK_WRTOSK(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200625 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_seek, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200626
627 HA_ATOMIC_INC(&lock_stats[lbl].num_seek_locked);
628
629 HA_ATOMIC_OR(&l->info.cur_seeker, tid_bit);
630 HA_ATOMIC_AND(&l->info.cur_writer, ~tid_bit);
631 l->info.last_location.function = func;
632 l->info.last_location.file = file;
633 l->info.last_location.line = line;
634
635 HA_ATOMIC_AND(&l->info.wait_seekers, ~tid_bit);
636}
637
638void __ha_rwlock_sklock(enum lock_label lbl, struct ha_rwlock *l,
639 const char *func, const char *file, int line)
640{
641 uint64_t start_time;
642
643 if ((l->info.cur_readers | l->info.cur_seeker | l->info.cur_writer) & tid_bit)
644 abort();
645
646 HA_ATOMIC_OR(&l->info.wait_seekers, tid_bit);
647
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200648 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200649 __RWLOCK_SKLOCK(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200650 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_seek, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200651
652 HA_ATOMIC_INC(&lock_stats[lbl].num_seek_locked);
653
654 HA_ATOMIC_OR(&l->info.cur_seeker, tid_bit);
655 l->info.last_location.function = func;
656 l->info.last_location.file = file;
657 l->info.last_location.line = line;
658
659 HA_ATOMIC_AND(&l->info.wait_seekers, ~tid_bit);
660}
661
662void __ha_rwlock_sktowr(enum lock_label lbl, struct ha_rwlock *l,
663 const char *func, const char *file, int line)
664{
665 uint64_t start_time;
666
667 if ((l->info.cur_readers | l->info.cur_writer) & tid_bit)
668 abort();
669
670 if (!(l->info.cur_seeker & tid_bit))
671 abort();
672
673 HA_ATOMIC_OR(&l->info.wait_writers, tid_bit);
674
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200675 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200676 __RWLOCK_SKTOWR(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200677 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_write, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200678
679 HA_ATOMIC_INC(&lock_stats[lbl].num_write_locked);
680
681 HA_ATOMIC_OR(&l->info.cur_writer, tid_bit);
682 HA_ATOMIC_AND(&l->info.cur_seeker, ~tid_bit);
683 l->info.last_location.function = func;
684 l->info.last_location.file = file;
685 l->info.last_location.line = line;
686
687 HA_ATOMIC_AND(&l->info.wait_writers, ~tid_bit);
688}
689
690void __ha_rwlock_sktord(enum lock_label lbl, struct ha_rwlock *l,
691 const char *func, const char *file, int line)
692{
693 uint64_t start_time;
694
695 if ((l->info.cur_readers | l->info.cur_writer) & tid_bit)
696 abort();
697
698 if (!(l->info.cur_seeker & tid_bit))
699 abort();
700
701 HA_ATOMIC_OR(&l->info.wait_readers, tid_bit);
702
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200703 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200704 __RWLOCK_SKTORD(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200705 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_read, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200706
707 HA_ATOMIC_INC(&lock_stats[lbl].num_read_locked);
708
709 HA_ATOMIC_OR(&l->info.cur_readers, tid_bit);
710 HA_ATOMIC_AND(&l->info.cur_seeker, ~tid_bit);
711 l->info.last_location.function = func;
712 l->info.last_location.file = file;
713 l->info.last_location.line = line;
714
715 HA_ATOMIC_AND(&l->info.wait_readers, ~tid_bit);
716}
717
718void __ha_rwlock_skunlock(enum lock_label lbl,struct ha_rwlock *l,
719 const char *func, const char *file, int line)
720{
721 if (!(l->info.cur_seeker & tid_bit))
722 abort();
723
724 HA_ATOMIC_AND(&l->info.cur_seeker, ~tid_bit);
725 l->info.last_location.function = func;
726 l->info.last_location.file = file;
727 l->info.last_location.line = line;
728
729 __RWLOCK_SKUNLOCK(&l->lock);
730
731 HA_ATOMIC_INC(&lock_stats[lbl].num_seek_unlocked);
732}
733
734int __ha_rwlock_trysklock(enum lock_label lbl, struct ha_rwlock *l,
735 const char *func, const char *file, int line)
736{
737 uint64_t start_time;
738 int r;
739
740 if ((l->info.cur_readers | l->info.cur_seeker | l->info.cur_writer) & tid_bit)
741 abort();
742
743 HA_ATOMIC_OR(&l->info.wait_seekers, tid_bit);
744
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200745 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200746 r = __RWLOCK_TRYSKLOCK(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200747 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_seek, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200748
749 if (likely(!r)) {
750 /* got the lock ! */
751 HA_ATOMIC_INC(&lock_stats[lbl].num_seek_locked);
752 HA_ATOMIC_OR(&l->info.cur_seeker, tid_bit);
753 l->info.last_location.function = func;
754 l->info.last_location.file = file;
755 l->info.last_location.line = line;
756 }
757
758 HA_ATOMIC_AND(&l->info.wait_seekers, ~tid_bit);
759 return r;
760}
761
762int __ha_rwlock_tryrdtosk(enum lock_label lbl, struct ha_rwlock *l,
763 const char *func, const char *file, int line)
764{
765 uint64_t start_time;
766 int r;
767
768 if ((l->info.cur_writer | l->info.cur_seeker) & tid_bit)
769 abort();
770
771 if (!(l->info.cur_readers & tid_bit))
772 abort();
773
774 HA_ATOMIC_OR(&l->info.wait_seekers, tid_bit);
775
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200776 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200777 r = __RWLOCK_TRYRDTOSK(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200778 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_seek, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200779
780 if (likely(!r)) {
781 /* got the lock ! */
782 HA_ATOMIC_INC(&lock_stats[lbl].num_seek_locked);
783 HA_ATOMIC_OR(&l->info.cur_seeker, tid_bit);
784 HA_ATOMIC_AND(&l->info.cur_readers, ~tid_bit);
785 l->info.last_location.function = func;
786 l->info.last_location.file = file;
787 l->info.last_location.line = line;
788 }
789
790 HA_ATOMIC_AND(&l->info.wait_seekers, ~tid_bit);
791 return r;
792}
793
794void __spin_init(struct ha_spinlock *l)
795{
796 memset(l, 0, sizeof(struct ha_spinlock));
797 __SPIN_INIT(&l->lock);
798}
799
800void __spin_destroy(struct ha_spinlock *l)
801{
802 __SPIN_DESTROY(&l->lock);
803 memset(l, 0, sizeof(struct ha_spinlock));
804}
805
806void __spin_lock(enum lock_label lbl, struct ha_spinlock *l,
807 const char *func, const char *file, int line)
808{
809 uint64_t start_time;
810
811 if (unlikely(l->info.owner & tid_bit)) {
812 /* the thread is already owning the lock */
813 abort();
814 }
815
816 HA_ATOMIC_OR(&l->info.waiters, tid_bit);
817
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200818 start_time = now_mono_time();
Willy Tarreau407ef892021-10-05 18:39:27 +0200819 __SPIN_LOCK(&l->lock);
Willy Tarreaudced3eb2021-10-05 18:48:23 +0200820 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_write, (now_mono_time() - start_time));
Willy Tarreau407ef892021-10-05 18:39:27 +0200821
822 HA_ATOMIC_INC(&lock_stats[lbl].num_write_locked);
823
824
825 l->info.owner = tid_bit;
826 l->info.last_location.function = func;
827 l->info.last_location.file = file;
828 l->info.last_location.line = line;
829
830 HA_ATOMIC_AND(&l->info.waiters, ~tid_bit);
831}
832
833int __spin_trylock(enum lock_label lbl, struct ha_spinlock *l,
834 const char *func, const char *file, int line)
835{
836 int r;
837
838 if (unlikely(l->info.owner & tid_bit)) {
839 /* the thread is already owning the lock */
840 abort();
841 }
842
843 /* try read should never wait */
844 r = __SPIN_TRYLOCK(&l->lock);
845 if (unlikely(r))
846 return r;
847 HA_ATOMIC_INC(&lock_stats[lbl].num_write_locked);
848
849 l->info.owner = tid_bit;
850 l->info.last_location.function = func;
851 l->info.last_location.file = file;
852 l->info.last_location.line = line;
853
854 return 0;
855}
856
857void __spin_unlock(enum lock_label lbl, struct ha_spinlock *l,
858 const char *func, const char *file, int line)
859{
860 if (unlikely(!(l->info.owner & tid_bit))) {
861 /* the thread is not owning the lock */
862 abort();
863 }
864
865 l->info.owner = 0;
866 l->info.last_location.function = func;
867 l->info.last_location.file = file;
868 l->info.last_location.line = line;
869
870 __SPIN_UNLOCK(&l->lock);
871 HA_ATOMIC_INC(&lock_stats[lbl].num_write_unlocked);
872}
873
874#endif // defined(DEBUG_THREAD) || defined(DEBUG_FULL)
875
Willy Tarreauf734ebf2020-09-09 17:07:54 +0200876/* Depending on the platform and how libpthread was built, pthread_exit() may
877 * involve some code in libgcc_s that would be loaded on exit for the first
878 * time, causing aborts if the process is chrooted. It's harmless bit very
879 * dirty. There isn't much we can do to make sure libgcc_s is loaded only if
880 * needed, so what we do here is that during early boot we create a dummy
881 * thread that immediately exits. This will lead to libgcc_s being loaded
882 * during boot on the platforms where it's required.
883 */
884static void *dummy_thread_function(void *data)
885{
886 pthread_exit(NULL);
887 return NULL;
888}
889
890static inline void preload_libgcc_s(void)
891{
892 pthread_t dummy_thread;
893 pthread_create(&dummy_thread, NULL, dummy_thread_function, NULL);
894 pthread_join(dummy_thread, NULL);
895}
896
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200897__attribute__((constructor))
Willy Tarreau3f567e42020-05-28 15:29:19 +0200898static void __thread_init(void)
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200899{
Willy Tarreauf5809cd2019-01-26 13:35:03 +0100900 char *ptr = NULL;
901
902 if (MAX_THREADS < 1 || MAX_THREADS > LONGBITS) {
903 ha_alert("MAX_THREADS value must be between 1 and %d inclusive; "
904 "HAProxy was built with value %d, please fix it and rebuild.\n",
905 LONGBITS, MAX_THREADS);
906 exit(1);
907 }
Willy Tarreau149ab772019-01-26 14:27:06 +0100908
Willy Tarreauf734ebf2020-09-09 17:07:54 +0200909 preload_libgcc_s();
Willy Tarreau77b98222020-09-02 08:04:35 +0200910
Willy Tarreau149ab772019-01-26 14:27:06 +0100911 thread_cpus_enabled_at_boot = thread_cpus_enabled();
912
913 memprintf(&ptr, "Built with multi-threading support (MAX_THREADS=%d, default=%d).",
914 MAX_THREADS, thread_cpus_enabled_at_boot);
Willy Tarreauf5809cd2019-01-26 13:35:03 +0100915 hap_register_build_opts(ptr, 1);
916
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200917#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
918 memset(lock_stats, 0, sizeof(lock_stats));
919#endif
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200920}
921
Willy Tarreau8459f252018-12-15 16:48:14 +0100922#else
923
924REGISTER_BUILD_OPTS("Built without multi-threading support (USE_THREAD not set).");
925
Willy Tarreau0ccd3222018-07-30 10:34:35 +0200926#endif // USE_THREAD
927
928
Willy Tarreau51ec03a2021-09-22 11:55:22 +0200929/* Parse the "nbthread" global directive, which takes an integer argument that
930 * contains the desired number of threads.
Willy Tarreau0ccd3222018-07-30 10:34:35 +0200931 */
Willy Tarreau51ec03a2021-09-22 11:55:22 +0200932static int cfg_parse_nbthread(char **args, int section_type, struct proxy *curpx,
933 const struct proxy *defpx, const char *file, int line,
934 char **err)
Willy Tarreau0ccd3222018-07-30 10:34:35 +0200935{
936 long nbthread;
937 char *errptr;
938
Willy Tarreau51ec03a2021-09-22 11:55:22 +0200939 if (too_many_args(1, args, err, NULL))
940 return -1;
941
942 nbthread = strtol(args[1], &errptr, 10);
943 if (!*args[1] || *errptr) {
944 memprintf(err, "'%s' passed a missing or unparsable integer value in '%s'", args[0], args[1]);
945 return -1;
Willy Tarreau0ccd3222018-07-30 10:34:35 +0200946 }
947
948#ifndef USE_THREAD
949 if (nbthread != 1) {
Willy Tarreau51ec03a2021-09-22 11:55:22 +0200950 memprintf(err, "'%s' specified with a value other than 1 while HAProxy is not compiled with threads support. Please check build options for USE_THREAD", args[0]);
951 return -1;
Willy Tarreau0ccd3222018-07-30 10:34:35 +0200952 }
953#else
954 if (nbthread < 1 || nbthread > MAX_THREADS) {
Willy Tarreau51ec03a2021-09-22 11:55:22 +0200955 memprintf(err, "'%s' value must be between 1 and %d (was %ld)", args[0], MAX_THREADS, nbthread);
956 return -1;
Willy Tarreau0ccd3222018-07-30 10:34:35 +0200957 }
958
Willy Tarreaufc647362019-02-02 17:05:03 +0100959 all_threads_mask = nbits(nbthread);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200960#endif
Willy Tarreau51ec03a2021-09-22 11:55:22 +0200961
962 HA_DIAG_WARNING_COND(global.nbthread,
963 "parsing [%s:%d] : nbthread is already defined and will be overridden.\n",
964 file, line);
965
966 global.nbthread = nbthread;
967 return 0;
Willy Tarreau0ccd3222018-07-30 10:34:35 +0200968}
Willy Tarreau51ec03a2021-09-22 11:55:22 +0200969
970/* config keyword parsers */
971static struct cfg_kw_list cfg_kws = {ILH, {
972 { CFG_GLOBAL, "nbthread", cfg_parse_nbthread, 0 },
973 { 0, NULL, NULL }
974}};
975
976INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);