blob: 77bd617aa836d0d75523795863df690826edbff0 [file] [log] [blame]
Christopher Faulet1a2b56e2017-10-12 16:09:09 +02001/*
2 * include/common/hathreads.h
3 * definitions, macros and inline functions about threads.
4 *
5 * Copyright (C) 2017 Christopher Fauet - cfaulet@haproxy.com
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation, version 2.1
10 * exclusively.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#ifndef _COMMON_HATHREADS_H
23#define _COMMON_HATHREADS_H
24
Willy Tarreau2beaaf72019-05-22 08:43:34 +020025#include <signal.h>
Willy Tarreau38171da2019-05-17 16:33:13 +020026#include <unistd.h>
27#ifdef _POSIX_PRIORITY_SCHEDULING
28#include <sched.h>
29#endif
30
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020031#include <common/config.h>
Willy Tarreau90fa97b2018-11-25 19:46:08 +010032#include <common/initcall.h>
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020033
Willy Tarreau0ccd3222018-07-30 10:34:35 +020034/* Note about all_threads_mask :
Willy Tarreauda9e9392019-02-02 17:03:41 +010035 * - this variable is comprised between 1 and LONGBITS.
36 * - with threads support disabled, this symbol is defined as constant 1UL.
37 * - with threads enabled, it contains the mask of enabled threads. Thus if
38 * only one thread is enabled, it equals 1.
Willy Tarreau0ccd3222018-07-30 10:34:35 +020039 */
40
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020041#ifndef USE_THREAD
42
Willy Tarreau421f02e2018-01-20 18:19:22 +010043#define MAX_THREADS 1
Willy Tarreau0c026f42018-08-01 19:12:20 +020044#define MAX_THREADS_MASK 1
45
46/* Only way found to replace variables with constants that are optimized away
47 * at build time.
48 */
49enum { all_threads_mask = 1UL };
Willy Tarreau441259c2019-05-22 07:48:18 +020050enum { threads_harmless_mask = 0 };
51enum { threads_want_rdv_mask = 0 };
Willy Tarreau0c026f42018-08-01 19:12:20 +020052enum { tid_bit = 1UL };
53enum { tid = 0 };
Willy Tarreau421f02e2018-01-20 18:19:22 +010054
Willy Tarreau5a6e2242019-05-20 18:56:48 +020055extern struct thread_info {
Willy Tarreau624dcbf2019-05-20 20:23:06 +020056 clockid_t clock_id;
Willy Tarreau81036f22019-05-20 19:24:50 +020057 uint64_t prev_cpu_time; /* previous per thread CPU time */
58 uint64_t prev_mono_time; /* previous system wide monotonic time */
59 unsigned int idle_pct; /* idle to total ratio over last sample (percent) */
Willy Tarreau5a6e2242019-05-20 18:56:48 +020060 /* pad to cache line (64B) */
61 char __pad[0]; /* unused except to check remaining room */
62 char __end[0] __attribute__((aligned(64)));
63} thread_info[MAX_THREADS];
64
Willy Tarreau8323a372019-05-20 18:57:53 +020065extern THREAD_LOCAL struct thread_info *ti; /* thread_info for the current thread */
66
Christopher Faulet9dcf9b62017-11-13 10:34:01 +010067#define __decl_hathreads(decl)
Willy Tarreau90fa97b2018-11-25 19:46:08 +010068#define __decl_spinlock(lock)
69#define __decl_aligned_spinlock(lock)
70#define __decl_rwlock(lock)
71#define __decl_aligned_rwlock(lock)
Christopher Faulet9dcf9b62017-11-13 10:34:01 +010072
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020073#define HA_ATOMIC_CAS(val, old, new) ({((*val) == (*old)) ? (*(val) = (new) , 1) : (*(old) = *(val), 0);})
Willy Tarreau6a38b322019-05-11 18:04:24 +020074#define HA_ATOMIC_DWCAS(val, o, n) ({((*val) == (*o)) ? (*(val) = (n) , 1) : (*(o) = *(val), 0);})
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020075#define HA_ATOMIC_ADD(val, i) ({*(val) += (i);})
76#define HA_ATOMIC_SUB(val, i) ({*(val) -= (i);})
Willy Tarreau9378df82018-09-05 16:11:03 +020077#define HA_ATOMIC_XADD(val, i) \
78 ({ \
79 typeof((val)) __p_xadd = (val); \
80 typeof(*(val)) __old_xadd = *__p_xadd; \
81 *__p_xadd += i; \
82 __old_xadd; \
83 })
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020084#define HA_ATOMIC_AND(val, flags) ({*(val) &= (flags);})
85#define HA_ATOMIC_OR(val, flags) ({*(val) |= (flags);})
86#define HA_ATOMIC_XCHG(val, new) \
87 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +020088 typeof(*(val)) __old_xchg = *(val); \
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020089 *(val) = new; \
Christopher Faulet48aa13f2018-04-09 08:45:43 +020090 __old_xchg; \
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020091 })
Willy Tarreau5266b3e2018-01-25 17:43:58 +010092#define HA_ATOMIC_BTS(val, bit) \
93 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +020094 typeof((val)) __p_bts = (val); \
95 typeof(*__p_bts) __b_bts = (1UL << (bit)); \
96 typeof(*__p_bts) __t_bts = *__p_bts & __b_bts; \
97 if (!__t_bts) \
98 *__p_bts |= __b_bts; \
99 __t_bts; \
Willy Tarreau5266b3e2018-01-25 17:43:58 +0100100 })
101#define HA_ATOMIC_BTR(val, bit) \
102 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200103 typeof((val)) __p_btr = (val); \
104 typeof(*__p_btr) __b_btr = (1UL << (bit)); \
105 typeof(*__p_btr) __t_btr = *__p_btr & __b_btr; \
106 if (__t_btr) \
107 *__p_btr &= ~__b_btr; \
108 __t_btr; \
Willy Tarreau5266b3e2018-01-25 17:43:58 +0100109 })
Olivier Houchard9ce62b52019-04-30 13:38:02 +0200110#define HA_ATOMIC_LOAD(val) *(val)
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200111#define HA_ATOMIC_STORE(val, new) ({*(val) = new;})
112#define HA_ATOMIC_UPDATE_MAX(val, new) \
113 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200114 typeof(*(val)) __new_max = (new); \
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200115 \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200116 if (*(val) < __new_max) \
117 *(val) = __new_max; \
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200118 *(val); \
119 })
120
121#define HA_ATOMIC_UPDATE_MIN(val, new) \
122 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200123 typeof(*(val)) __new_min = (new); \
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200124 \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200125 if (*(val) > __new_min) \
126 *(val) = __new_min; \
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200127 *(val); \
128 })
129
Willy Tarreaub29dc952017-10-31 18:00:20 +0100130#define HA_BARRIER() do { } while (0)
Christopher Faulet339fff82017-10-19 11:59:15 +0200131
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100132#define HA_SPIN_INIT(l) do { /* do nothing */ } while(0)
133#define HA_SPIN_DESTROY(l) do { /* do nothing */ } while(0)
134#define HA_SPIN_LOCK(lbl, l) do { /* do nothing */ } while(0)
135#define HA_SPIN_TRYLOCK(lbl, l) ({ 0; })
136#define HA_SPIN_UNLOCK(lbl, l) do { /* do nothing */ } while(0)
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200137
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100138#define HA_RWLOCK_INIT(l) do { /* do nothing */ } while(0)
139#define HA_RWLOCK_DESTROY(l) do { /* do nothing */ } while(0)
140#define HA_RWLOCK_WRLOCK(lbl, l) do { /* do nothing */ } while(0)
141#define HA_RWLOCK_TRYWRLOCK(lbl, l) ({ 0; })
142#define HA_RWLOCK_WRUNLOCK(lbl, l) do { /* do nothing */ } while(0)
143#define HA_RWLOCK_RDLOCK(lbl, l) do { /* do nothing */ } while(0)
144#define HA_RWLOCK_TRYRDLOCK(lbl, l) ({ 0; })
145#define HA_RWLOCK_RDUNLOCK(lbl, l) do { /* do nothing */ } while(0)
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200146
William Lallemand6e1796e2018-06-07 11:23:40 +0200147#define ha_sigmask(how, set, oldset) sigprocmask(how, set, oldset)
148
Willy Tarreau0c026f42018-08-01 19:12:20 +0200149static inline void ha_set_tid(unsigned int tid)
150{
Willy Tarreau8323a372019-05-20 18:57:53 +0200151 ti = &thread_info[tid];
Willy Tarreau38171da2019-05-17 16:33:13 +0200152}
153
154static inline void ha_thread_relax(void)
155{
156#if _POSIX_PRIORITY_SCHEDULING
157 sched_yield();
158#endif
Willy Tarreau0c026f42018-08-01 19:12:20 +0200159}
William Lallemand6e1796e2018-06-07 11:23:40 +0200160
Willy Tarreau2beaaf72019-05-22 08:43:34 +0200161/* send signal <sig> to thread <thr> */
162static inline void ha_tkill(unsigned int thr, int sig)
163{
164 raise(sig);
165}
166
167/* send signal <sig> to all threads */
168static inline void ha_tkillall(int sig)
169{
170 raise(sig);
171}
172
Olivier Houchard9abcf6e2019-03-07 18:45:00 +0100173static inline void __ha_barrier_atomic_load(void)
174{
175}
176
177static inline void __ha_barrier_atomic_store(void)
178{
179}
180
181static inline void __ha_barrier_atomic_full(void)
182{
183}
184
Olivier Houchardf61f0cb2017-12-21 17:13:05 +0100185static inline void __ha_barrier_load(void)
186{
187}
188
189static inline void __ha_barrier_store(void)
190{
191}
192
193static inline void __ha_barrier_full(void)
194{
195}
196
Willy Tarreau60b639c2018-08-02 10:16:17 +0200197static inline void thread_harmless_now()
198{
199}
200
201static inline void thread_harmless_end()
202{
203}
204
205static inline void thread_isolate()
206{
207}
208
209static inline void thread_release()
210{
211}
212
213static inline unsigned long thread_isolated()
214{
215 return 1;
216}
217
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200218#else /* USE_THREAD */
219
220#include <stdio.h>
221#include <stdlib.h>
222#include <string.h>
223#include <pthread.h>
224#include <import/plock.h>
225
Willy Tarreauf5809cd2019-01-26 13:35:03 +0100226#ifndef MAX_THREADS
Willy Tarreau421f02e2018-01-20 18:19:22 +0100227#define MAX_THREADS LONGBITS
Willy Tarreauf5809cd2019-01-26 13:35:03 +0100228#endif
229
230#define MAX_THREADS_MASK (~0UL >> (LONGBITS - MAX_THREADS))
Willy Tarreau421f02e2018-01-20 18:19:22 +0100231
Christopher Faulet9dcf9b62017-11-13 10:34:01 +0100232#define __decl_hathreads(decl) decl
233
Willy Tarreau90fa97b2018-11-25 19:46:08 +0100234/* declare a self-initializing spinlock */
235#define __decl_spinlock(lock) \
236 HA_SPINLOCK_T (lock); \
237 INITCALL1(STG_LOCK, ha_spin_init, &(lock))
238
239/* declare a self-initializing spinlock, aligned on a cache line */
240#define __decl_aligned_spinlock(lock) \
241 HA_SPINLOCK_T (lock) __attribute__((aligned(64))); \
242 INITCALL1(STG_LOCK, ha_spin_init, &(lock))
243
244/* declare a self-initializing rwlock */
245#define __decl_rwlock(lock) \
246 HA_RWLOCK_T (lock); \
247 INITCALL1(STG_LOCK, ha_rwlock_init, &(lock))
248
249/* declare a self-initializing rwlock, aligned on a cache line */
250#define __decl_aligned_rwlock(lock) \
251 HA_RWLOCK_T (lock) __attribute__((aligned(64))); \
252 INITCALL1(STG_LOCK, ha_rwlock_init, &(lock))
253
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200254/* TODO: thread: For now, we rely on GCC builtins but it could be a good idea to
255 * have a header file regrouping all functions dealing with threads. */
Willy Tarreau1a69af62018-01-04 18:49:31 +0100256
David Carlierec5e8452018-01-11 14:20:43 +0000257#if defined(__GNUC__) && (__GNUC__ < 4 || __GNUC__ == 4 && __GNUC_MINOR__ < 7) && !defined(__clang__)
Willy Tarreau1a69af62018-01-04 18:49:31 +0100258/* gcc < 4.7 */
259
260#define HA_ATOMIC_ADD(val, i) __sync_add_and_fetch(val, i)
261#define HA_ATOMIC_SUB(val, i) __sync_sub_and_fetch(val, i)
Willy Tarreau9378df82018-09-05 16:11:03 +0200262#define HA_ATOMIC_XADD(val, i) __sync_fetch_and_add(val, i)
Willy Tarreau1a69af62018-01-04 18:49:31 +0100263#define HA_ATOMIC_AND(val, flags) __sync_and_and_fetch(val, flags)
264#define HA_ATOMIC_OR(val, flags) __sync_or_and_fetch(val, flags)
265
266/* the CAS is a bit complicated. The older API doesn't support returning the
267 * value and the swap's result at the same time. So here we take what looks
268 * like the safest route, consisting in using the boolean version guaranteeing
269 * that the operation was performed or not, and we snoop a previous value. If
270 * the compare succeeds, we return. If it fails, we return the previous value,
271 * but only if it differs from the expected one. If it's the same it's a race
272 * thus we try again to avoid confusing a possibly sensitive caller.
273 */
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200274#define HA_ATOMIC_CAS(val, old, new) \
275 ({ \
276 typeof((val)) __val_cas = (val); \
277 typeof((old)) __oldp_cas = (old); \
278 typeof(*(old)) __oldv_cas; \
279 typeof((new)) __new_cas = (new); \
280 int __ret_cas; \
281 do { \
282 __oldv_cas = *__val_cas; \
283 __ret_cas = __sync_bool_compare_and_swap(__val_cas, *__oldp_cas, __new_cas); \
284 } while (!__ret_cas && *__oldp_cas == __oldv_cas); \
285 if (!__ret_cas) \
286 *__oldp_cas = __oldv_cas; \
287 __ret_cas; \
Willy Tarreau1a69af62018-01-04 18:49:31 +0100288 })
289
Willy Tarreau6a38b322019-05-11 18:04:24 +0200290#define HA_ATOMIC_DWCAS(val, o, n) __ha_cas_dw(val, o, n)
291
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200292#define HA_ATOMIC_XCHG(val, new) \
293 ({ \
294 typeof((val)) __val_xchg = (val); \
295 typeof(*(val)) __old_xchg; \
296 typeof((new)) __new_xchg = (new); \
297 do { __old_xchg = *__val_xchg; \
298 } while (!__sync_bool_compare_and_swap(__val_xchg, __old_xchg, __new_xchg)); \
299 __old_xchg; \
Willy Tarreau1a69af62018-01-04 18:49:31 +0100300 })
Willy Tarreau5266b3e2018-01-25 17:43:58 +0100301
302#define HA_ATOMIC_BTS(val, bit) \
303 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200304 typeof(*(val)) __b_bts = (1UL << (bit)); \
305 __sync_fetch_and_or((val), __b_bts) & __b_bts; \
Willy Tarreau5266b3e2018-01-25 17:43:58 +0100306 })
307
308#define HA_ATOMIC_BTR(val, bit) \
309 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200310 typeof(*(val)) __b_btr = (1UL << (bit)); \
311 __sync_fetch_and_and((val), ~__b_btr) & __b_btr; \
Willy Tarreau5266b3e2018-01-25 17:43:58 +0100312 })
313
Olivier Houchard9ce62b52019-04-30 13:38:02 +0200314#define HA_ATOMIC_LOAD(val) \
315 ({ \
316 typeof(*(val)) ret; \
317 __sync_synchronize(); \
318 ret = *(volatile typeof(val))val; \
319 __sync_synchronize(); \
320 ret; \
321 })
322
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200323#define HA_ATOMIC_STORE(val, new) \
324 ({ \
325 typeof((val)) __val_store = (val); \
326 typeof(*(val)) __old_store; \
327 typeof((new)) __new_store = (new); \
328 do { __old_store = *__val_store; \
329 } while (!__sync_bool_compare_and_swap(__val_store, __old_store, __new_store)); \
Willy Tarreau1a69af62018-01-04 18:49:31 +0100330 })
331#else
332/* gcc >= 4.7 */
Olivier Houchard11353792019-03-07 18:48:22 +0100333#define HA_ATOMIC_CAS(val, old, new) __atomic_compare_exchange_n(val, old, new, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)
Willy Tarreau6a38b322019-05-11 18:04:24 +0200334#define HA_ATOMIC_DWCAS(val, o, n) __ha_cas_dw(val, o, n)
Olivier Houchard11353792019-03-07 18:48:22 +0100335#define HA_ATOMIC_ADD(val, i) __atomic_add_fetch(val, i, __ATOMIC_SEQ_CST)
336#define HA_ATOMIC_XADD(val, i) __atomic_fetch_add(val, i, __ATOMIC_SEQ_CST)
337#define HA_ATOMIC_SUB(val, i) __atomic_sub_fetch(val, i, __ATOMIC_SEQ_CST)
338#define HA_ATOMIC_AND(val, flags) __atomic_and_fetch(val, flags, __ATOMIC_SEQ_CST)
339#define HA_ATOMIC_OR(val, flags) __atomic_or_fetch(val, flags, __ATOMIC_SEQ_CST)
Willy Tarreau5266b3e2018-01-25 17:43:58 +0100340#define HA_ATOMIC_BTS(val, bit) \
341 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200342 typeof(*(val)) __b_bts = (1UL << (bit)); \
343 __sync_fetch_and_or((val), __b_bts) & __b_bts; \
Willy Tarreau5266b3e2018-01-25 17:43:58 +0100344 })
345
346#define HA_ATOMIC_BTR(val, bit) \
347 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200348 typeof(*(val)) __b_btr = (1UL << (bit)); \
349 __sync_fetch_and_and((val), ~__b_btr) & __b_btr; \
Willy Tarreau5266b3e2018-01-25 17:43:58 +0100350 })
351
Olivier Houchard11353792019-03-07 18:48:22 +0100352#define HA_ATOMIC_XCHG(val, new) __atomic_exchange_n(val, new, __ATOMIC_SEQ_CST)
353#define HA_ATOMIC_STORE(val, new) __atomic_store_n(val, new, __ATOMIC_SEQ_CST)
Olivier Houchard9ce62b52019-04-30 13:38:02 +0200354#define HA_ATOMIC_LOAD(val) __atomic_load_n(val, __ATOMIC_SEQ_CST)
Olivier Houchard3212a2c2019-04-15 21:14:25 +0200355
356/* Variants that don't generate any memory barrier.
357 * If you're unsure how to deal with barriers, just use the HA_ATOMIC_* version,
358 * that will always generate correct code.
359 * Usually it's fine to use those when updating data that have no dependency,
360 * ie updating a counter. Otherwise a barrier is required.
361 */
362#define _HA_ATOMIC_CAS(val, old, new) __atomic_compare_exchange_n(val, old, new, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)
Willy Tarreau6a38b322019-05-11 18:04:24 +0200363#define _HA_ATOMIC_DWCAS(val, o, n) __ha_cas_dw(val, o, n)
Olivier Houchard3212a2c2019-04-15 21:14:25 +0200364#define _HA_ATOMIC_ADD(val, i) __atomic_add_fetch(val, i, __ATOMIC_RELAXED)
365#define _HA_ATOMIC_XADD(val, i) __atomic_fetch_add(val, i, __ATOMIC_RELAXED)
366#define _HA_ATOMIC_SUB(val, i) __atomic_sub_fetch(val, i, __ATOMIC_RELAXED)
367#define _HA_ATOMIC_AND(val, flags) __atomic_and_fetch(val, flags, __ATOMIC_RELAXED)
368#define _HA_ATOMIC_OR(val, flags) __atomic_or_fetch(val, flags, __ATOMIC_RELAXED)
369#define _HA_ATOMIC_XCHG(val, new) __atomic_exchange_n(val, new, __ATOMIC_RELAXED)
370#define _HA_ATOMIC_STORE(val, new) __atomic_store_n(val, new, __ATOMIC_RELAXED)
Olivier Houchard9ce62b52019-04-30 13:38:02 +0200371#define _HA_ATOMIC_LOAD(val) __atomic_load_n(val, __ATOMIC_RELAXED)
Olivier Houchard3212a2c2019-04-15 21:14:25 +0200372
373#endif /* gcc >= 4.7 */
Willy Tarreau1a69af62018-01-04 18:49:31 +0100374
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200375#define HA_ATOMIC_UPDATE_MAX(val, new) \
376 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200377 typeof(*(val)) __old_max = *(val); \
378 typeof(*(val)) __new_max = (new); \
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200379 \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200380 while (__old_max < __new_max && \
381 !HA_ATOMIC_CAS(val, &__old_max, __new_max)); \
382 *(val); \
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200383 })
384#define HA_ATOMIC_UPDATE_MIN(val, new) \
385 ({ \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200386 typeof(*(val)) __old_min = *(val); \
387 typeof(*(val)) __new_min = (new); \
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200388 \
Christopher Faulet48aa13f2018-04-09 08:45:43 +0200389 while (__old_min > __new_min && \
390 !HA_ATOMIC_CAS(val, &__old_min, __new_min)); \
391 *(val); \
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200392 })
393
Willy Tarreaub29dc952017-10-31 18:00:20 +0100394#define HA_BARRIER() pl_barrier()
395
Willy Tarreau60b639c2018-08-02 10:16:17 +0200396void thread_harmless_till_end();
397void thread_isolate();
398void thread_release();
Willy Tarreau2beaaf72019-05-22 08:43:34 +0200399void ha_tkill(unsigned int thr, int sig);
400void ha_tkillall(int sig);
Christopher Faulet339fff82017-10-19 11:59:15 +0200401
Willy Tarreau5a6e2242019-05-20 18:56:48 +0200402extern struct thread_info {
403 pthread_t pthread;
404 clockid_t clock_id;
Willy Tarreau81036f22019-05-20 19:24:50 +0200405 uint64_t prev_cpu_time; /* previous per thread CPU time */
406 uint64_t prev_mono_time; /* previous system wide monotonic time */
407 unsigned int idle_pct; /* idle to total ratio over last sample (percent) */
Willy Tarreau5a6e2242019-05-20 18:56:48 +0200408 /* pad to cache line (64B) */
409 char __pad[0]; /* unused except to check remaining room */
410 char __end[0] __attribute__((aligned(64)));
411} thread_info[MAX_THREADS];
412
Willy Tarreau0c026f42018-08-01 19:12:20 +0200413extern THREAD_LOCAL unsigned int tid; /* The thread id */
414extern THREAD_LOCAL unsigned long tid_bit; /* The bit corresponding to the thread id */
Willy Tarreau8323a372019-05-20 18:57:53 +0200415extern THREAD_LOCAL struct thread_info *ti; /* thread_info for the current thread */
Christopher Fauletddb6c162018-07-20 09:31:53 +0200416extern volatile unsigned long all_threads_mask;
Willy Tarreau60b639c2018-08-02 10:16:17 +0200417extern volatile unsigned long threads_want_rdv_mask;
418extern volatile unsigned long threads_harmless_mask;
419
420/* explanation for threads_want_rdv_mask and threads_harmless_mask :
421 * - threads_want_rdv_mask is a bit field indicating all threads that have
422 * requested a rendez-vous of other threads using thread_isolate().
423 * - threads_harmless_mask is a bit field indicating all threads that are
424 * currently harmless in that they promise not to access a shared resource.
425 *
426 * For a given thread, its bits in want_rdv and harmless can be translated like
427 * this :
428 *
429 * ----------+----------+----------------------------------------------------
430 * want_rdv | harmless | description
431 * ----------+----------+----------------------------------------------------
432 * 0 | 0 | thread not interested in RDV, possibly harmful
433 * 0 | 1 | thread not interested in RDV but harmless
434 * 1 | 1 | thread interested in RDV and waiting for its turn
435 * 1 | 0 | thread currently working isolated from others
436 * ----------+----------+----------------------------------------------------
437 */
Olivier Houchard6b96f722018-04-25 16:58:25 +0200438
William Lallemand6e1796e2018-06-07 11:23:40 +0200439#define ha_sigmask(how, set, oldset) pthread_sigmask(how, set, oldset)
440
Willy Tarreau0c026f42018-08-01 19:12:20 +0200441/* sets the thread ID and the TID bit for the current thread */
442static inline void ha_set_tid(unsigned int data)
443{
444 tid = data;
445 tid_bit = (1UL << tid);
Willy Tarreau8323a372019-05-20 18:57:53 +0200446 ti = &thread_info[tid];
Willy Tarreau0c026f42018-08-01 19:12:20 +0200447}
448
Willy Tarreau38171da2019-05-17 16:33:13 +0200449static inline void ha_thread_relax(void)
450{
451#if _POSIX_PRIORITY_SCHEDULING
452 sched_yield();
453#else
454 pl_cpu_relax();
455#endif
456}
457
Willy Tarreau60b639c2018-08-02 10:16:17 +0200458/* Marks the thread as harmless. Note: this must be true, i.e. the thread must
459 * not be touching any unprotected shared resource during this period. Usually
460 * this is called before poll(), but it may also be placed around very slow
461 * calls (eg: some crypto operations). Needs to be terminated using
462 * thread_harmless_end().
463 */
464static inline void thread_harmless_now()
465{
466 HA_ATOMIC_OR(&threads_harmless_mask, tid_bit);
467}
468
469/* Ends the harmless period started by thread_harmless_now(). Usually this is
470 * placed after the poll() call. If it is discovered that a job was running and
471 * is relying on the thread still being harmless, the thread waits for the
472 * other one to finish.
473 */
474static inline void thread_harmless_end()
475{
476 while (1) {
477 HA_ATOMIC_AND(&threads_harmless_mask, ~tid_bit);
478 if (likely((threads_want_rdv_mask & all_threads_mask) == 0))
479 break;
480 thread_harmless_till_end();
481 }
482}
483
484/* an isolated thread has harmless cleared and want_rdv set */
485static inline unsigned long thread_isolated()
486{
487 return threads_want_rdv_mask & ~threads_harmless_mask & tid_bit;
488}
489
William Lallemand6e1796e2018-06-07 11:23:40 +0200490
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200491#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
492
Christopher Fauletf51bac22018-01-30 11:04:29 +0100493/* WARNING!!! if you update this enum, please also keep lock_label() up to date below */
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200494enum lock_label {
Christopher Fauletd4604ad2017-05-29 10:40:41 +0200495 FD_LOCK,
Emeric Brunc60def82017-09-27 14:59:38 +0200496 TASK_RQ_LOCK,
497 TASK_WQ_LOCK,
Christopher Fauletb349e482017-08-29 09:52:38 +0200498 POOL_LOCK,
Christopher Faulet8d8aa0d2017-05-30 15:36:50 +0200499 LISTENER_LOCK,
Christopher Fauletff8abcd2017-06-02 15:33:24 +0200500 PROXY_LOCK,
Christopher Faulet29f77e82017-06-08 14:04:45 +0200501 SERVER_LOCK,
Christopher Faulet5b517552017-06-09 14:17:53 +0200502 LBPRM_LOCK,
Christopher Fauletb79a94c2017-05-30 15:34:30 +0200503 SIGNALS_LOCK,
Emeric Brun819fc6f2017-06-13 19:37:32 +0200504 STK_TABLE_LOCK,
505 STK_SESS_LOCK,
Emeric Brun1138fd02017-06-19 12:38:55 +0200506 APPLETS_LOCK,
Emeric Brun80527f52017-06-19 17:46:37 +0200507 PEER_LOCK,
Emeric Bruna1dd2432017-06-21 15:42:52 +0200508 BUF_WQ_LOCK,
Emeric Brun6b35e9b2017-06-30 16:23:45 +0200509 STRMS_LOCK,
Emeric Brun821bb9b2017-06-15 16:37:39 +0200510 SSL_LOCK,
511 SSL_GEN_CERTS_LOCK,
Emeric Brunb5997f72017-07-03 11:34:05 +0200512 PATREF_LOCK,
513 PATEXP_LOCK,
514 PATLRU_LOCK,
Christopher Faulete95f2c32017-07-24 16:30:34 +0200515 VARS_LOCK,
Christopher Faulet8ca3b4b2017-07-25 11:07:15 +0200516 COMP_POOL_LOCK,
Thierry FOURNIER61ba0e22017-07-12 11:41:21 +0200517 LUA_LOCK,
Thierry FOURNIER738a6d72017-07-17 00:14:07 +0200518 NOTIF_LOCK,
Christopher Faulet24289f22017-09-25 14:48:02 +0200519 SPOE_APPLET_LOCK,
Christopher Fauletb2812a62017-10-04 16:17:58 +0200520 DNS_LOCK,
Christopher Fauletcfda8472017-10-20 15:40:23 +0200521 PID_LIST_LOCK,
Christopher Fauletc2a89a62017-10-23 15:54:24 +0200522 EMAIL_ALERTS_LOCK,
Emeric Brund8b3b652017-11-07 11:19:48 +0100523 PIPES_LOCK,
Christopher Faulet16f45c82018-02-16 11:23:49 +0100524 TLSKEYS_REF_LOCK,
Willy Tarreau34d4b522018-10-29 18:02:54 +0100525 AUTH_LOCK,
Frédéric Lécailled803e472019-04-25 07:42:09 +0200526 LOGSRV_LOCK,
Ben51Degrees4ddf59d2019-02-05 13:24:00 +0000527 OTHER_LOCK,
Christopher Faulet339fff82017-10-19 11:59:15 +0200528 LOCK_LABELS
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200529};
530struct lock_stat {
531 uint64_t nsec_wait_for_write;
532 uint64_t nsec_wait_for_read;
533 uint64_t num_write_locked;
534 uint64_t num_write_unlocked;
535 uint64_t num_read_locked;
536 uint64_t num_read_unlocked;
537};
538
539extern struct lock_stat lock_stats[LOCK_LABELS];
540
541#define __HA_SPINLOCK_T unsigned long
542
543#define __SPIN_INIT(l) ({ (*l) = 0; })
544#define __SPIN_DESTROY(l) ({ (*l) = 0; })
Willy Tarreau88ac59b2017-11-06 01:03:26 +0100545#define __SPIN_LOCK(l) pl_take_s(l)
546#define __SPIN_TRYLOCK(l) !pl_try_s(l)
547#define __SPIN_UNLOCK(l) pl_drop_s(l)
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200548
549#define __HA_RWLOCK_T unsigned long
550
551#define __RWLOCK_INIT(l) ({ (*l) = 0; })
552#define __RWLOCK_DESTROY(l) ({ (*l) = 0; })
553#define __RWLOCK_WRLOCK(l) pl_take_w(l)
554#define __RWLOCK_TRYWRLOCK(l) !pl_try_w(l)
555#define __RWLOCK_WRUNLOCK(l) pl_drop_w(l)
556#define __RWLOCK_RDLOCK(l) pl_take_r(l)
557#define __RWLOCK_TRYRDLOCK(l) !pl_try_r(l)
558#define __RWLOCK_RDUNLOCK(l) pl_drop_r(l)
559
560#define HA_SPINLOCK_T struct ha_spinlock
561
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100562#define HA_SPIN_INIT(l) __spin_init(l)
563#define HA_SPIN_DESTROY(l) __spin_destroy(l)
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200564
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100565#define HA_SPIN_LOCK(lbl, l) __spin_lock(lbl, l, __func__, __FILE__, __LINE__)
566#define HA_SPIN_TRYLOCK(lbl, l) __spin_trylock(lbl, l, __func__, __FILE__, __LINE__)
567#define HA_SPIN_UNLOCK(lbl, l) __spin_unlock(lbl, l, __func__, __FILE__, __LINE__)
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200568
569#define HA_RWLOCK_T struct ha_rwlock
570
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100571#define HA_RWLOCK_INIT(l) __ha_rwlock_init((l))
572#define HA_RWLOCK_DESTROY(l) __ha_rwlock_destroy((l))
573#define HA_RWLOCK_WRLOCK(lbl,l) __ha_rwlock_wrlock(lbl, l, __func__, __FILE__, __LINE__)
574#define HA_RWLOCK_TRYWRLOCK(lbl,l) __ha_rwlock_trywrlock(lbl, l, __func__, __FILE__, __LINE__)
575#define HA_RWLOCK_WRUNLOCK(lbl,l) __ha_rwlock_wrunlock(lbl, l, __func__, __FILE__, __LINE__)
576#define HA_RWLOCK_RDLOCK(lbl,l) __ha_rwlock_rdlock(lbl, l)
577#define HA_RWLOCK_TRYRDLOCK(lbl,l) __ha_rwlock_tryrdlock(lbl, l)
578#define HA_RWLOCK_RDUNLOCK(lbl,l) __ha_rwlock_rdunlock(lbl, l)
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200579
580struct ha_spinlock {
581 __HA_SPINLOCK_T lock;
582 struct {
583 unsigned long owner; /* a bit is set to 1 << tid for the lock owner */
584 unsigned long waiters; /* a bit is set to 1 << tid for waiting threads */
585 struct {
586 const char *function;
587 const char *file;
588 int line;
589 } last_location; /* location of the last owner */
590 } info;
591};
592
593struct ha_rwlock {
594 __HA_RWLOCK_T lock;
595 struct {
596 unsigned long cur_writer; /* a bit is set to 1 << tid for the lock owner */
597 unsigned long wait_writers; /* a bit is set to 1 << tid for waiting writers */
598 unsigned long cur_readers; /* a bit is set to 1 << tid for current readers */
599 unsigned long wait_readers; /* a bit is set to 1 << tid for waiting waiters */
600 struct {
601 const char *function;
602 const char *file;
603 int line;
604 } last_location; /* location of the last write owner */
605 } info;
606};
607
Christopher Fauletf51bac22018-01-30 11:04:29 +0100608static inline const char *lock_label(enum lock_label label)
609{
610 switch (label) {
Christopher Fauletf51bac22018-01-30 11:04:29 +0100611 case FD_LOCK: return "FD";
612 case TASK_RQ_LOCK: return "TASK_RQ";
613 case TASK_WQ_LOCK: return "TASK_WQ";
614 case POOL_LOCK: return "POOL";
615 case LISTENER_LOCK: return "LISTENER";
Christopher Fauletf51bac22018-01-30 11:04:29 +0100616 case PROXY_LOCK: return "PROXY";
617 case SERVER_LOCK: return "SERVER";
Christopher Fauletf51bac22018-01-30 11:04:29 +0100618 case LBPRM_LOCK: return "LBPRM";
619 case SIGNALS_LOCK: return "SIGNALS";
620 case STK_TABLE_LOCK: return "STK_TABLE";
621 case STK_SESS_LOCK: return "STK_SESS";
622 case APPLETS_LOCK: return "APPLETS";
623 case PEER_LOCK: return "PEER";
624 case BUF_WQ_LOCK: return "BUF_WQ";
625 case STRMS_LOCK: return "STRMS";
626 case SSL_LOCK: return "SSL";
627 case SSL_GEN_CERTS_LOCK: return "SSL_GEN_CERTS";
628 case PATREF_LOCK: return "PATREF";
629 case PATEXP_LOCK: return "PATEXP";
630 case PATLRU_LOCK: return "PATLRU";
631 case VARS_LOCK: return "VARS";
632 case COMP_POOL_LOCK: return "COMP_POOL";
633 case LUA_LOCK: return "LUA";
634 case NOTIF_LOCK: return "NOTIF";
635 case SPOE_APPLET_LOCK: return "SPOE_APPLET";
636 case DNS_LOCK: return "DNS";
637 case PID_LIST_LOCK: return "PID_LIST";
638 case EMAIL_ALERTS_LOCK: return "EMAIL_ALERTS";
639 case PIPES_LOCK: return "PIPES";
Christopher Faulet16f45c82018-02-16 11:23:49 +0100640 case TLSKEYS_REF_LOCK: return "TLSKEYS_REF";
Willy Tarreau34d4b522018-10-29 18:02:54 +0100641 case AUTH_LOCK: return "AUTH";
Frédéric Lécailled803e472019-04-25 07:42:09 +0200642 case LOGSRV_LOCK: return "LOGSRV";
Ben51Degrees4ddf59d2019-02-05 13:24:00 +0000643 case OTHER_LOCK: return "OTHER";
Christopher Fauletf51bac22018-01-30 11:04:29 +0100644 case LOCK_LABELS: break; /* keep compiler happy */
645 };
646 /* only way to come here is consecutive to an internal bug */
647 abort();
648}
649
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200650static inline void show_lock_stats()
651{
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200652 int lbl;
653
654 for (lbl = 0; lbl < LOCK_LABELS; lbl++) {
655 fprintf(stderr,
656 "Stats about Lock %s: \n"
657 "\t # write lock : %lu\n"
658 "\t # write unlock: %lu (%ld)\n"
659 "\t # wait time for write : %.3f msec\n"
660 "\t # wait time for write/lock: %.3f nsec\n"
661 "\t # read lock : %lu\n"
662 "\t # read unlock : %lu (%ld)\n"
663 "\t # wait time for read : %.3f msec\n"
664 "\t # wait time for read/lock : %.3f nsec\n",
Christopher Fauletf51bac22018-01-30 11:04:29 +0100665 lock_label(lbl),
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200666 lock_stats[lbl].num_write_locked,
667 lock_stats[lbl].num_write_unlocked,
668 lock_stats[lbl].num_write_unlocked - lock_stats[lbl].num_write_locked,
669 (double)lock_stats[lbl].nsec_wait_for_write / 1000000.0,
670 lock_stats[lbl].num_write_locked ? ((double)lock_stats[lbl].nsec_wait_for_write / (double)lock_stats[lbl].num_write_locked) : 0,
671 lock_stats[lbl].num_read_locked,
672 lock_stats[lbl].num_read_unlocked,
673 lock_stats[lbl].num_read_unlocked - lock_stats[lbl].num_read_locked,
674 (double)lock_stats[lbl].nsec_wait_for_read / 1000000.0,
675 lock_stats[lbl].num_read_locked ? ((double)lock_stats[lbl].nsec_wait_for_read / (double)lock_stats[lbl].num_read_locked) : 0);
676 }
677}
678
679/* Following functions are used to collect some stats about locks. We wrap
680 * pthread functions to known how much time we wait in a lock. */
681
682static uint64_t nsec_now(void) {
683 struct timespec ts;
684
685 clock_gettime(CLOCK_MONOTONIC, &ts);
686 return ((uint64_t) ts.tv_sec * 1000000000ULL +
687 (uint64_t) ts.tv_nsec);
688}
689
690static inline void __ha_rwlock_init(struct ha_rwlock *l)
691{
692 memset(l, 0, sizeof(struct ha_rwlock));
693 __RWLOCK_INIT(&l->lock);
694}
695
696static inline void __ha_rwlock_destroy(struct ha_rwlock *l)
697{
698 __RWLOCK_DESTROY(&l->lock);
699 memset(l, 0, sizeof(struct ha_rwlock));
700}
701
702
703static inline void __ha_rwlock_wrlock(enum lock_label lbl, struct ha_rwlock *l,
704 const char *func, const char *file, int line)
705{
706 uint64_t start_time;
707
708 if (unlikely(l->info.cur_writer & tid_bit)) {
709 /* the thread is already owning the lock for write */
710 abort();
711 }
712
713 if (unlikely(l->info.cur_readers & tid_bit)) {
714 /* the thread is already owning the lock for read */
715 abort();
716 }
717
718 HA_ATOMIC_OR(&l->info.wait_writers, tid_bit);
719
720 start_time = nsec_now();
721 __RWLOCK_WRLOCK(&l->lock);
722 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_write, (nsec_now() - start_time));
723
724 HA_ATOMIC_ADD(&lock_stats[lbl].num_write_locked, 1);
725
726 l->info.cur_writer = tid_bit;
727 l->info.last_location.function = func;
728 l->info.last_location.file = file;
729 l->info.last_location.line = line;
730
731 HA_ATOMIC_AND(&l->info.wait_writers, ~tid_bit);
732}
733
734static inline int __ha_rwlock_trywrlock(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 (unlikely(l->info.cur_writer & tid_bit)) {
741 /* the thread is already owning the lock for write */
742 abort();
743 }
744
745 if (unlikely(l->info.cur_readers & tid_bit)) {
746 /* the thread is already owning the lock for read */
747 abort();
748 }
749
750 /* We set waiting writer because trywrlock could wait for readers to quit */
751 HA_ATOMIC_OR(&l->info.wait_writers, tid_bit);
752
753 start_time = nsec_now();
754 r = __RWLOCK_TRYWRLOCK(&l->lock);
755 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_write, (nsec_now() - start_time));
756 if (unlikely(r)) {
757 HA_ATOMIC_AND(&l->info.wait_writers, ~tid_bit);
758 return r;
759 }
760 HA_ATOMIC_ADD(&lock_stats[lbl].num_write_locked, 1);
761
762 l->info.cur_writer = tid_bit;
763 l->info.last_location.function = func;
764 l->info.last_location.file = file;
765 l->info.last_location.line = line;
766
767 HA_ATOMIC_AND(&l->info.wait_writers, ~tid_bit);
768
769 return 0;
770}
771
772static inline void __ha_rwlock_wrunlock(enum lock_label lbl,struct ha_rwlock *l,
773 const char *func, const char *file, int line)
774{
775 if (unlikely(!(l->info.cur_writer & tid_bit))) {
776 /* the thread is not owning the lock for write */
777 abort();
778 }
779
780 l->info.cur_writer = 0;
781 l->info.last_location.function = func;
782 l->info.last_location.file = file;
783 l->info.last_location.line = line;
784
785 __RWLOCK_WRUNLOCK(&l->lock);
786
787 HA_ATOMIC_ADD(&lock_stats[lbl].num_write_unlocked, 1);
788}
789
790static inline void __ha_rwlock_rdlock(enum lock_label lbl,struct ha_rwlock *l)
791{
792 uint64_t start_time;
793
794 if (unlikely(l->info.cur_writer & tid_bit)) {
795 /* the thread is already owning the lock for write */
796 abort();
797 }
798
799 if (unlikely(l->info.cur_readers & tid_bit)) {
800 /* the thread is already owning the lock for read */
801 abort();
802 }
803
804 HA_ATOMIC_OR(&l->info.wait_readers, tid_bit);
805
806 start_time = nsec_now();
807 __RWLOCK_RDLOCK(&l->lock);
808 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_read, (nsec_now() - start_time));
809 HA_ATOMIC_ADD(&lock_stats[lbl].num_read_locked, 1);
810
811 HA_ATOMIC_OR(&l->info.cur_readers, tid_bit);
812
813 HA_ATOMIC_AND(&l->info.wait_readers, ~tid_bit);
814}
815
816static inline int __ha_rwlock_tryrdlock(enum lock_label lbl,struct ha_rwlock *l)
817{
818 int r;
819
820 if (unlikely(l->info.cur_writer & tid_bit)) {
821 /* the thread is already owning the lock for write */
822 abort();
823 }
824
825 if (unlikely(l->info.cur_readers & tid_bit)) {
826 /* the thread is already owning the lock for read */
827 abort();
828 }
829
830 /* try read should never wait */
831 r = __RWLOCK_TRYRDLOCK(&l->lock);
832 if (unlikely(r))
833 return r;
834 HA_ATOMIC_ADD(&lock_stats[lbl].num_read_locked, 1);
835
836 HA_ATOMIC_OR(&l->info.cur_readers, tid_bit);
837
838 return 0;
839}
840
841static inline void __ha_rwlock_rdunlock(enum lock_label lbl,struct ha_rwlock *l)
842{
843 if (unlikely(!(l->info.cur_readers & tid_bit))) {
844 /* the thread is not owning the lock for read */
845 abort();
846 }
847
848 HA_ATOMIC_AND(&l->info.cur_readers, ~tid_bit);
849
850 __RWLOCK_RDUNLOCK(&l->lock);
851
852 HA_ATOMIC_ADD(&lock_stats[lbl].num_read_unlocked, 1);
853}
854
855static inline void __spin_init(struct ha_spinlock *l)
856{
857 memset(l, 0, sizeof(struct ha_spinlock));
858 __SPIN_INIT(&l->lock);
859}
860
861static inline void __spin_destroy(struct ha_spinlock *l)
862{
863 __SPIN_DESTROY(&l->lock);
864 memset(l, 0, sizeof(struct ha_spinlock));
865}
866
867static inline void __spin_lock(enum lock_label lbl, struct ha_spinlock *l,
868 const char *func, const char *file, int line)
869{
870 uint64_t start_time;
871
872 if (unlikely(l->info.owner & tid_bit)) {
873 /* the thread is already owning the lock */
874 abort();
875 }
876
877 HA_ATOMIC_OR(&l->info.waiters, tid_bit);
878
879 start_time = nsec_now();
880 __SPIN_LOCK(&l->lock);
881 HA_ATOMIC_ADD(&lock_stats[lbl].nsec_wait_for_write, (nsec_now() - start_time));
882
883 HA_ATOMIC_ADD(&lock_stats[lbl].num_write_locked, 1);
884
885
886 l->info.owner = tid_bit;
887 l->info.last_location.function = func;
888 l->info.last_location.file = file;
889 l->info.last_location.line = line;
890
891 HA_ATOMIC_AND(&l->info.waiters, ~tid_bit);
892}
893
894static inline int __spin_trylock(enum lock_label lbl, struct ha_spinlock *l,
895 const char *func, const char *file, int line)
896{
897 int r;
898
899 if (unlikely(l->info.owner & tid_bit)) {
900 /* the thread is already owning the lock */
901 abort();
902 }
903
904 /* try read should never wait */
905 r = __SPIN_TRYLOCK(&l->lock);
906 if (unlikely(r))
907 return r;
908 HA_ATOMIC_ADD(&lock_stats[lbl].num_write_locked, 1);
909
910 l->info.owner = tid_bit;
911 l->info.last_location.function = func;
912 l->info.last_location.file = file;
913 l->info.last_location.line = line;
914
915 return 0;
916}
917
918static inline void __spin_unlock(enum lock_label lbl, struct ha_spinlock *l,
919 const char *func, const char *file, int line)
920{
921 if (unlikely(!(l->info.owner & tid_bit))) {
922 /* the thread is not owning the lock */
923 abort();
924 }
925
926 l->info.owner = 0;
927 l->info.last_location.function = func;
928 l->info.last_location.file = file;
929 l->info.last_location.line = line;
930
Willy Tarreau7c2a2ad2017-11-02 16:26:02 +0100931 __SPIN_UNLOCK(&l->lock);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200932 HA_ATOMIC_ADD(&lock_stats[lbl].num_write_unlocked, 1);
933}
934
935#else /* DEBUG_THREAD */
936
937#define HA_SPINLOCK_T unsigned long
938
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100939#define HA_SPIN_INIT(l) ({ (*l) = 0; })
940#define HA_SPIN_DESTROY(l) ({ (*l) = 0; })
941#define HA_SPIN_LOCK(lbl, l) pl_take_s(l)
942#define HA_SPIN_TRYLOCK(lbl, l) !pl_try_s(l)
943#define HA_SPIN_UNLOCK(lbl, l) pl_drop_s(l)
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200944
945#define HA_RWLOCK_T unsigned long
946
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100947#define HA_RWLOCK_INIT(l) ({ (*l) = 0; })
948#define HA_RWLOCK_DESTROY(l) ({ (*l) = 0; })
949#define HA_RWLOCK_WRLOCK(lbl,l) pl_take_w(l)
950#define HA_RWLOCK_TRYWRLOCK(lbl,l) !pl_try_w(l)
951#define HA_RWLOCK_WRUNLOCK(lbl,l) pl_drop_w(l)
952#define HA_RWLOCK_RDLOCK(lbl,l) pl_take_r(l)
953#define HA_RWLOCK_TRYRDLOCK(lbl,l) !pl_try_r(l)
954#define HA_RWLOCK_RDUNLOCK(lbl,l) pl_drop_r(l)
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200955
956#endif /* DEBUG_THREAD */
957
Olivier Houchardf61f0cb2017-12-21 17:13:05 +0100958#ifdef __x86_64__
Willy Tarreau2325d8a2018-10-10 18:29:23 +0200959
Olivier Houchardf61f0cb2017-12-21 17:13:05 +0100960static __inline int
961__ha_cas_dw(void *target, void *compare, const void *set)
962{
963 char ret;
964
965 __asm __volatile("lock cmpxchg16b %0; setz %3"
966 : "+m" (*(void **)target),
967 "=a" (((void **)compare)[0]),
968 "=d" (((void **)compare)[1]),
969 "=q" (ret)
970 : "a" (((void **)compare)[0]),
971 "d" (((void **)compare)[1]),
972 "b" (((const void **)set)[0]),
973 "c" (((const void **)set)[1])
974 : "memory", "cc");
975 return (ret);
976}
977
Olivier Houchard9abcf6e2019-03-07 18:45:00 +0100978/* Use __ha_barrier_atomic* when you're trying to protect data that are
979 * are modified using HA_ATOMIC* (except HA_ATOMIC_STORE)
980 */
981static __inline void
982__ha_barrier_atomic_load(void)
983{
984 __asm __volatile("" ::: "memory");
985}
986
Olivier Houchardf61f0cb2017-12-21 17:13:05 +0100987static __inline void
Olivier Houchard9abcf6e2019-03-07 18:45:00 +0100988__ha_barrier_atomic_store(void)
989{
990 __asm __volatile("" ::: "memory");
991}
992
993static __inline void
994__ha_barrier_atomic_full(void)
995{
996 __asm __volatile("" ::: "memory");
997}
998
999static __inline void
Olivier Houchardf61f0cb2017-12-21 17:13:05 +01001000__ha_barrier_load(void)
1001{
1002 __asm __volatile("lfence" ::: "memory");
1003}
1004
1005static __inline void
1006__ha_barrier_store(void)
1007{
1008 __asm __volatile("sfence" ::: "memory");
1009}
1010
1011static __inline void
1012__ha_barrier_full(void)
1013{
1014 __asm __volatile("mfence" ::: "memory");
1015}
1016
1017#elif defined(__arm__) && (defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__))
Willy Tarreau2325d8a2018-10-10 18:29:23 +02001018
Olivier Houchard9abcf6e2019-03-07 18:45:00 +01001019/* Use __ha_barrier_atomic* when you're trying to protect data that are
1020 * are modified using HA_ATOMIC* (except HA_ATOMIC_STORE)
1021 */
1022static __inline void
1023__ha_barrier_atomic_load(void)
1024{
1025 __asm __volatile("dmb" ::: "memory");
1026}
1027
1028static __inline void
1029__ha_barrier_atomic_store(void)
1030{
1031 __asm __volatile("dsb" ::: "memory");
1032}
1033
1034static __inline void
1035__ha_barrier_atomic_full(void)
1036{
1037 __asm __volatile("dmb" ::: "memory");
1038}
1039
Olivier Houchardf61f0cb2017-12-21 17:13:05 +01001040static __inline void
1041__ha_barrier_load(void)
1042{
1043 __asm __volatile("dmb" ::: "memory");
1044}
1045
1046static __inline void
1047__ha_barrier_store(void)
1048{
1049 __asm __volatile("dsb" ::: "memory");
1050}
1051
1052static __inline void
1053__ha_barrier_full(void)
1054{
1055 __asm __volatile("dmb" ::: "memory");
1056}
1057
Willy Tarreau41ccb192018-02-14 14:16:28 +01001058static __inline int __ha_cas_dw(void *target, void *compare, const void *set)
Olivier Houchardf61f0cb2017-12-21 17:13:05 +01001059{
1060 uint64_t previous;
1061 int tmp;
1062
1063 __asm __volatile("1:"
1064 "ldrexd %0, [%4];"
1065 "cmp %Q0, %Q2;"
1066 "ittt eq;"
1067 "cmpeq %R0, %R2;"
1068 "strexdeq %1, %3, [%4];"
1069 "cmpeq %1, #1;"
1070 "beq 1b;"
1071 : "=&r" (previous), "=&r" (tmp)
Willy Tarreau41ccb192018-02-14 14:16:28 +01001072 : "r" (*(uint64_t *)compare), "r" (*(uint64_t *)set), "r" (target)
Olivier Houchardf61f0cb2017-12-21 17:13:05 +01001073 : "memory", "cc");
1074 tmp = (previous == *(uint64_t *)compare);
1075 *(uint64_t *)compare = previous;
1076 return (tmp);
1077}
1078
1079#elif defined (__aarch64__)
Olivier Houchardf61f0cb2017-12-21 17:13:05 +01001080
Olivier Houchard9abcf6e2019-03-07 18:45:00 +01001081/* Use __ha_barrier_atomic* when you're trying to protect data that are
1082 * are modified using HA_ATOMIC* (except HA_ATOMIC_STORE)
1083 */
1084static __inline void
1085__ha_barrier_atomic_load(void)
1086{
1087 __asm __volatile("dmb ishld" ::: "memory");
1088}
1089
1090static __inline void
1091__ha_barrier_atomic_store(void)
1092{
1093 __asm __volatile("dmb ishst" ::: "memory");
1094}
1095
1096static __inline void
1097__ha_barrier_atomic_full(void)
1098{
1099 __asm __volatile("dmb ish" ::: "memory");
1100}
1101
Olivier Houchardf61f0cb2017-12-21 17:13:05 +01001102static __inline void
1103__ha_barrier_load(void)
1104{
1105 __asm __volatile("dmb ishld" ::: "memory");
1106}
1107
1108static __inline void
1109__ha_barrier_store(void)
1110{
1111 __asm __volatile("dmb ishst" ::: "memory");
1112}
1113
1114static __inline void
1115__ha_barrier_full(void)
1116{
1117 __asm __volatile("dmb ish" ::: "memory");
1118}
1119
1120static __inline int __ha_cas_dw(void *target, void *compare, void *set)
1121{
1122 void *value[2];
1123 uint64_t tmp1, tmp2;
1124
1125 __asm__ __volatile__("1:"
1126 "ldxp %0, %1, [%4];"
1127 "mov %2, %0;"
1128 "mov %3, %1;"
1129 "eor %0, %0, %5;"
1130 "eor %1, %1, %6;"
1131 "orr %1, %0, %1;"
1132 "mov %w0, #0;"
1133 "cbnz %1, 2f;"
1134 "stxp %w0, %7, %8, [%4];"
1135 "cbnz %w0, 1b;"
1136 "mov %w0, #1;"
1137 "2:"
1138 : "=&r" (tmp1), "=&r" (tmp2), "=&r" (value[0]), "=&r" (value[1])
1139 : "r" (target), "r" (((void **)(compare))[0]), "r" (((void **)(compare))[1]), "r" (((void **)(set))[0]), "r" (((void **)(set))[1])
1140 : "cc", "memory");
1141
1142 memcpy(compare, &value, sizeof(value));
1143 return (tmp1);
1144}
1145
1146#else
Olivier Houchard9abcf6e2019-03-07 18:45:00 +01001147#define __ha_barrier_atomic_load __sync_synchronize
1148#define __ha_barrier_atomic_store __sync_synchronize
1149#define __ha_barrier_atomic_full __sync_synchronize
Olivier Houchardf61f0cb2017-12-21 17:13:05 +01001150#define __ha_barrier_load __sync_synchronize
1151#define __ha_barrier_store __sync_synchronize
1152#define __ha_barrier_full __sync_synchronize
1153#endif
1154
Willy Tarreaua8ae77d2018-11-25 19:28:23 +01001155void ha_spin_init(HA_SPINLOCK_T *l);
1156void ha_rwlock_init(HA_RWLOCK_T *l);
1157
Christopher Faulet1a2b56e2017-10-12 16:09:09 +02001158#endif /* USE_THREAD */
1159
Willy Tarreau149ab772019-01-26 14:27:06 +01001160extern int thread_cpus_enabled_at_boot;
1161
Olivier Houchardf61f0cb2017-12-21 17:13:05 +01001162static inline void __ha_compiler_barrier(void)
1163{
1164 __asm __volatile("" ::: "memory");
1165}
1166
Willy Tarreau0ccd3222018-07-30 10:34:35 +02001167int parse_nbthread(const char *arg, char **err);
Willy Tarreau149ab772019-01-26 14:27:06 +01001168int thread_get_default_count();
Willy Tarreau4037a3f2018-03-28 18:06:47 +02001169
Olivier Houchardd0c3b882019-03-07 18:55:31 +01001170#ifndef _HA_ATOMIC_CAS
1171#define _HA_ATOMIC_CAS HA_ATOMIC_CAS
1172#endif /* !_HA_ATOMIC_CAS */
1173
Willy Tarreau6a38b322019-05-11 18:04:24 +02001174#ifndef _HA_ATOMIC_DWCAS
1175#define _HA_ATOMIC_DWCAS HA_ATOMIC_DWCAS
1176#endif /* !_HA_ATOMIC_CAS */
1177
Olivier Houchardd0c3b882019-03-07 18:55:31 +01001178#ifndef _HA_ATOMIC_ADD
1179#define _HA_ATOMIC_ADD HA_ATOMIC_ADD
1180#endif /* !_HA_ATOMIC_ADD */
1181
1182#ifndef _HA_ATOMIC_XADD
1183#define _HA_ATOMIC_XADD HA_ATOMIC_XADD
1184#endif /* !_HA_ATOMIC_SUB */
1185
1186#ifndef _HA_ATOMIC_SUB
1187#define _HA_ATOMIC_SUB HA_ATOMIC_SUB
1188#endif /* !_HA_ATOMIC_SUB */
1189
1190#ifndef _HA_ATOMIC_AND
1191#define _HA_ATOMIC_AND HA_ATOMIC_AND
1192#endif /* !_HA_ATOMIC_AND */
1193
1194#ifndef _HA_ATOMIC_OR
1195#define _HA_ATOMIC_OR HA_ATOMIC_OR
1196#endif /* !_HA_ATOMIC_OR */
1197
1198#ifndef _HA_ATOMIC_XCHG
1199#define _HA_ATOMIC_XCHG HA_ATOMIC_XCHG
1200#endif /* !_HA_ATOMIC_XCHG */
1201
1202#ifndef _HA_ATOMIC_STORE
1203#define _HA_ATOMIC_STORE HA_ATOMIC_STORE
1204#endif /* !_HA_ATOMIC_STORE */
Olivier Houchard9ce62b52019-04-30 13:38:02 +02001205
1206#ifndef _HA_ATOMIC_LOAD
1207#define _HA_ATOMIC_LOAD HA_ATOMIC_LOAD
1208#endif /* !_HA_ATOMIC_LOAD */
Christopher Faulet1a2b56e2017-10-12 16:09:09 +02001209#endif /* _COMMON_HATHREADS_H */