blob: bfd055aa9b9c56c2078a8175fe117d6272b2c9d4 [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
Christopher Faulet339fff82017-10-19 11:59:15 +020013#include <unistd.h>
Willy Tarreau0ccd3222018-07-30 10:34:35 +020014#include <stdlib.h>
Christopher Faulet339fff82017-10-19 11:59:15 +020015#include <fcntl.h>
16
Willy Tarreau04931492017-11-03 23:39:25 +010017#include <common/cfgparse.h>
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020018#include <common/hathreads.h>
Christopher Faulet339fff82017-10-19 11:59:15 +020019#include <common/standard.h>
20#include <proto/fd.h>
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020021
Christopher Faulete9a896e2017-11-14 10:16:04 +010022THREAD_LOCAL unsigned int tid = 0;
23THREAD_LOCAL unsigned long tid_bit = (1UL << 0);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020024
Willy Tarreaue96e61c2018-03-29 18:54:33 +020025/* Dummy I/O handler used by the sync pipe.*/
26void thread_sync_io_handler(int fd)
27{
28}
29
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020030#ifdef USE_THREAD
31
Christopher Faulet339fff82017-10-19 11:59:15 +020032static HA_SPINLOCK_T sync_lock;
33static int threads_sync_pipe[2];
34static unsigned long threads_want_sync = 0;
Willy Tarreau0ccd3222018-07-30 10:34:35 +020035volatile unsigned long all_threads_mask = 1; // nbthread 1 assumed by default
Christopher Faulet339fff82017-10-19 11:59:15 +020036
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020037#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
38struct lock_stat lock_stats[LOCK_LABELS];
39#endif
40
Olivier Houchard3e123042018-07-27 17:06:59 +020041/* Initializes the sync point. It creates a pipe used by threads to wake up all
42 * others when a sync is requested. It also initializes the mask of all created
Christopher Faulet339fff82017-10-19 11:59:15 +020043 * threads. It returns 0 on success and -1 if an error occurred.
44 */
Willy Tarreau0ccd3222018-07-30 10:34:35 +020045int thread_sync_init()
Christopher Faulet339fff82017-10-19 11:59:15 +020046{
47 int rfd;
48
49 if (pipe(threads_sync_pipe) < 0)
50 return -1;
51
52 rfd = threads_sync_pipe[0];
53 fcntl(rfd, F_SETFL, O_NONBLOCK);
Willy Tarreaua9786b62018-01-25 07:22:13 +010054 fd_insert(rfd, thread_sync_io_handler, thread_sync_io_handler, MAX_THREADS_MASK);
Christopher Faulet339fff82017-10-19 11:59:15 +020055 return 0;
56}
57
58/* Enables the sync point. */
59void thread_sync_enable(void)
60{
61 fd_want_recv(threads_sync_pipe[0]);
62}
63
64/* Called when a thread want to pass into the sync point. It subscribes the
65 * current thread in threads waiting for sync by update a bit-field. It this is
66 * the first one, it wakeup all other threads by writing on the sync pipe.
67 */
68void thread_want_sync()
69{
70 if (all_threads_mask) {
Christopher Faulet81991d32017-12-02 09:53:24 +010071 if (threads_want_sync & tid_bit)
72 return;
Christopher Faulet339fff82017-10-19 11:59:15 +020073 if (HA_ATOMIC_OR(&threads_want_sync, tid_bit) == tid_bit)
74 shut_your_big_mouth_gcc(write(threads_sync_pipe[1], "S", 1));
75 }
76 else {
77 threads_want_sync = 1;
78 }
79}
80
81/* Returns 1 if no thread has requested a sync. Otherwise, it returns 0. */
82int thread_no_sync()
83{
Christopher Faulet148b16e2018-05-02 16:58:40 +020084 return (threads_want_sync == 0UL);
Christopher Faulet339fff82017-10-19 11:59:15 +020085}
86
87/* Returns 1 if the current thread has requested a sync. Otherwise, it returns
88 * 0.
89 */
90int thread_need_sync()
91{
Christopher Faulet148b16e2018-05-02 16:58:40 +020092 return ((threads_want_sync & tid_bit) != 0UL);
Christopher Faulet339fff82017-10-19 11:59:15 +020093}
94
95/* Thread barrier. Synchronizes all threads at the barrier referenced by
96 * <barrier>. The calling thread shall block until all other threads have called
97 * thread_sync_barrier specifying the same barrier.
98 *
99 * If you need to use several barriers at differnt points, you need to use a
100 * different <barrier> for each point.
101 */
102static inline void thread_sync_barrier(volatile unsigned long *barrier)
103{
104 unsigned long old = all_threads_mask;
105
106 HA_ATOMIC_CAS(barrier, &old, 0);
Christopher Faulet209d02a2017-10-27 23:01:38 +0200107 HA_ATOMIC_OR(barrier, tid_bit);
Willy Tarreau3ea24902018-07-27 07:47:24 +0200108
109 /* Note below: we need to wait for all threads to join here, but in
110 * case several threads are scheduled on the same CPU, busy polling
111 * will instead degrade the performance, forcing other threads to
112 * wait longer (typically in epoll_wait()). Let's use sched_yield()
113 * when available instead.
114 */
115 while ((*barrier & all_threads_mask) != all_threads_mask) {
116#if _POSIX_PRIORITY_SCHEDULING
117 sched_yield();
118#else
Christopher Faulet339fff82017-10-19 11:59:15 +0200119 pl_cpu_relax();
Willy Tarreau3ea24902018-07-27 07:47:24 +0200120#endif
121 }
Christopher Faulet339fff82017-10-19 11:59:15 +0200122}
123
124/* Enter into the sync point and lock it if the current thread has requested a
125 * sync. */
126void thread_enter_sync()
127{
128 static volatile unsigned long barrier = 0;
129
130 if (!all_threads_mask)
131 return;
132
133 thread_sync_barrier(&barrier);
134 if (threads_want_sync & tid_bit)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100135 HA_SPIN_LOCK(THREAD_SYNC_LOCK, &sync_lock);
Christopher Faulet339fff82017-10-19 11:59:15 +0200136}
137
138/* Exit from the sync point and unlock it if it was previously locked. If the
139 * current thread is the last one to have requested a sync, the sync pipe is
140 * flushed.
141 */
142void thread_exit_sync()
143{
144 static volatile unsigned long barrier = 0;
145
146 if (!all_threads_mask)
147 return;
148
149 if (threads_want_sync & tid_bit)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100150 HA_SPIN_UNLOCK(THREAD_SYNC_LOCK, &sync_lock);
Christopher Faulet339fff82017-10-19 11:59:15 +0200151
152 if (HA_ATOMIC_AND(&threads_want_sync, ~tid_bit) == 0) {
153 char c;
154
155 shut_your_big_mouth_gcc(read(threads_sync_pipe[0], &c, 1));
156 fd_done_recv(threads_sync_pipe[0]);
157 }
158
159 thread_sync_barrier(&barrier);
160}
161
162
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200163__attribute__((constructor))
164static void __hathreads_init(void)
165{
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100166 HA_SPIN_INIT(&sync_lock);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200167#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
168 memset(lock_stats, 0, sizeof(lock_stats));
169#endif
Willy Tarreau6dbd3e92017-11-05 11:50:18 +0100170 hap_register_build_opts("Built with multi-threading support.", 0);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200171}
172
Willy Tarreau0ccd3222018-07-30 10:34:35 +0200173#endif // USE_THREAD
174
175
176/* Parse the number of threads in argument <arg>, returns it and adjusts a few
177 * internal variables accordingly, or fails and returns zero with an error
178 * reason in <errmsg>. May be called multiple times while parsing.
179 */
180int parse_nbthread(const char *arg, char **err)
181{
182 long nbthread;
183 char *errptr;
184
185 nbthread = strtol(arg, &errptr, 10);
186 if (!*arg || *errptr) {
187 memprintf(err, "passed a missing or unparsable integer value in '%s'", arg);
188 return 0;
189 }
190
191#ifndef USE_THREAD
192 if (nbthread != 1) {
193 memprintf(err, "specified with a value other than 1 while HAProxy is not compiled with threads support. Please check build options for USE_THREAD");
194 return 0;
195 }
196#else
197 if (nbthread < 1 || nbthread > MAX_THREADS) {
198 memprintf(err, "value must be between 1 and %d (was %ld)", MAX_THREADS, nbthread);
199 return 0;
200 }
201
202 /* we proceed like this to be sure never to overflow the left shift */
203 all_threads_mask = 1UL << (nbthread - 1);
204 all_threads_mask |= all_threads_mask - 1;
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200205#endif
Willy Tarreau0ccd3222018-07-30 10:34:35 +0200206 return nbthread;
207}