2017-10-12 14:09:09 +00:00
|
|
|
/*
|
|
|
|
* functions about threads.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017 Christopher Fauet - cfaulet@haproxy.com
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2017-10-19 09:59:15 +00:00
|
|
|
#include <unistd.h>
|
2018-07-30 08:34:35 +00:00
|
|
|
#include <stdlib.h>
|
2017-10-19 09:59:15 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
|
2017-11-03 22:39:25 +00:00
|
|
|
#include <common/cfgparse.h>
|
2017-10-12 14:09:09 +00:00
|
|
|
#include <common/hathreads.h>
|
2017-10-19 09:59:15 +00:00
|
|
|
#include <common/standard.h>
|
|
|
|
#include <proto/fd.h>
|
2017-10-12 14:09:09 +00:00
|
|
|
|
|
|
|
|
2018-03-29 16:54:33 +00:00
|
|
|
/* Dummy I/O handler used by the sync pipe.*/
|
|
|
|
void thread_sync_io_handler(int fd)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-10-12 14:09:09 +00:00
|
|
|
#ifdef USE_THREAD
|
|
|
|
|
2017-10-19 09:59:15 +00:00
|
|
|
static HA_SPINLOCK_T sync_lock;
|
|
|
|
static int threads_sync_pipe[2];
|
|
|
|
static unsigned long threads_want_sync = 0;
|
2018-07-30 08:34:35 +00:00
|
|
|
volatile unsigned long all_threads_mask = 1; // nbthread 1 assumed by default
|
2018-08-01 17:12:20 +00:00
|
|
|
THREAD_LOCAL unsigned int tid = 0;
|
|
|
|
THREAD_LOCAL unsigned long tid_bit = (1UL << 0);
|
|
|
|
|
2017-10-19 09:59:15 +00:00
|
|
|
|
2017-10-12 14:09:09 +00:00
|
|
|
#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
|
|
|
|
struct lock_stat lock_stats[LOCK_LABELS];
|
|
|
|
#endif
|
|
|
|
|
2018-07-27 15:06:59 +00:00
|
|
|
/* Initializes the sync point. It creates a pipe used by threads to wake up all
|
|
|
|
* others when a sync is requested. It also initializes the mask of all created
|
2017-10-19 09:59:15 +00:00
|
|
|
* threads. It returns 0 on success and -1 if an error occurred.
|
|
|
|
*/
|
2018-07-30 08:34:35 +00:00
|
|
|
int thread_sync_init()
|
2017-10-19 09:59:15 +00:00
|
|
|
{
|
|
|
|
int rfd;
|
|
|
|
|
|
|
|
if (pipe(threads_sync_pipe) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
rfd = threads_sync_pipe[0];
|
|
|
|
fcntl(rfd, F_SETFL, O_NONBLOCK);
|
2018-01-25 06:22:13 +00:00
|
|
|
fd_insert(rfd, thread_sync_io_handler, thread_sync_io_handler, MAX_THREADS_MASK);
|
2017-10-19 09:59:15 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enables the sync point. */
|
|
|
|
void thread_sync_enable(void)
|
|
|
|
{
|
|
|
|
fd_want_recv(threads_sync_pipe[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called when a thread want to pass into the sync point. It subscribes the
|
|
|
|
* current thread in threads waiting for sync by update a bit-field. It this is
|
|
|
|
* the first one, it wakeup all other threads by writing on the sync pipe.
|
|
|
|
*/
|
|
|
|
void thread_want_sync()
|
|
|
|
{
|
|
|
|
if (all_threads_mask) {
|
2017-12-02 08:53:24 +00:00
|
|
|
if (threads_want_sync & tid_bit)
|
|
|
|
return;
|
2017-10-19 09:59:15 +00:00
|
|
|
if (HA_ATOMIC_OR(&threads_want_sync, tid_bit) == tid_bit)
|
|
|
|
shut_your_big_mouth_gcc(write(threads_sync_pipe[1], "S", 1));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
threads_want_sync = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns 1 if no thread has requested a sync. Otherwise, it returns 0. */
|
|
|
|
int thread_no_sync()
|
|
|
|
{
|
BUG/MEDIUM: threads: Fix the sync point for more than 32 threads
In the sync point, to know if a thread has requested a synchronization, we call
the function thread_need_sync(). It should return 1 if yes, otherwise it should
return 0. It is intended to return a signed integer.
But internally, instead of returning 0 or 1, it returns 0 or tid_bit
(threads_want_sync & tid_bit). So, tid_bit is casted in integer. For the first
32 threads, it's ok, because we always check if thread_need_sync() returns
something else than 0. But this is a problem if HAProxy is started with more
than 32 threads, because for threads 33 to 64 (so for tid 32 to 63), their
tid_bit casted to integer are evaluated to 0. So the sync point does not work for
more than 32 threads.
Now, the function thread_need_sync() respects its contract, returning 0 or
1. the function thread_no_sync() has also been updated to avoid any ambiguities.
This patch must be backported in HAProxy 1.8.
2018-05-02 14:58:40 +00:00
|
|
|
return (threads_want_sync == 0UL);
|
2017-10-19 09:59:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns 1 if the current thread has requested a sync. Otherwise, it returns
|
|
|
|
* 0.
|
|
|
|
*/
|
|
|
|
int thread_need_sync()
|
|
|
|
{
|
BUG/MEDIUM: threads: Fix the sync point for more than 32 threads
In the sync point, to know if a thread has requested a synchronization, we call
the function thread_need_sync(). It should return 1 if yes, otherwise it should
return 0. It is intended to return a signed integer.
But internally, instead of returning 0 or 1, it returns 0 or tid_bit
(threads_want_sync & tid_bit). So, tid_bit is casted in integer. For the first
32 threads, it's ok, because we always check if thread_need_sync() returns
something else than 0. But this is a problem if HAProxy is started with more
than 32 threads, because for threads 33 to 64 (so for tid 32 to 63), their
tid_bit casted to integer are evaluated to 0. So the sync point does not work for
more than 32 threads.
Now, the function thread_need_sync() respects its contract, returning 0 or
1. the function thread_no_sync() has also been updated to avoid any ambiguities.
This patch must be backported in HAProxy 1.8.
2018-05-02 14:58:40 +00:00
|
|
|
return ((threads_want_sync & tid_bit) != 0UL);
|
2017-10-19 09:59:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Thread barrier. Synchronizes all threads at the barrier referenced by
|
|
|
|
* <barrier>. The calling thread shall block until all other threads have called
|
|
|
|
* thread_sync_barrier specifying the same barrier.
|
|
|
|
*
|
|
|
|
* If you need to use several barriers at differnt points, you need to use a
|
|
|
|
* different <barrier> for each point.
|
|
|
|
*/
|
|
|
|
static inline void thread_sync_barrier(volatile unsigned long *barrier)
|
|
|
|
{
|
|
|
|
unsigned long old = all_threads_mask;
|
|
|
|
|
|
|
|
HA_ATOMIC_CAS(barrier, &old, 0);
|
2017-10-27 21:01:38 +00:00
|
|
|
HA_ATOMIC_OR(barrier, tid_bit);
|
2018-07-27 05:47:24 +00:00
|
|
|
|
|
|
|
/* Note below: we need to wait for all threads to join here, but in
|
|
|
|
* case several threads are scheduled on the same CPU, busy polling
|
|
|
|
* will instead degrade the performance, forcing other threads to
|
|
|
|
* wait longer (typically in epoll_wait()). Let's use sched_yield()
|
|
|
|
* when available instead.
|
|
|
|
*/
|
|
|
|
while ((*barrier & all_threads_mask) != all_threads_mask) {
|
|
|
|
#if _POSIX_PRIORITY_SCHEDULING
|
|
|
|
sched_yield();
|
|
|
|
#else
|
2017-10-19 09:59:15 +00:00
|
|
|
pl_cpu_relax();
|
2018-07-27 05:47:24 +00:00
|
|
|
#endif
|
|
|
|
}
|
2017-10-19 09:59:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Enter into the sync point and lock it if the current thread has requested a
|
|
|
|
* sync. */
|
|
|
|
void thread_enter_sync()
|
|
|
|
{
|
|
|
|
static volatile unsigned long barrier = 0;
|
|
|
|
|
2018-08-01 17:12:20 +00:00
|
|
|
if (!(all_threads_mask & (all_threads_mask - 1)))
|
2017-10-19 09:59:15 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
thread_sync_barrier(&barrier);
|
|
|
|
if (threads_want_sync & tid_bit)
|
2017-11-07 09:42:54 +00:00
|
|
|
HA_SPIN_LOCK(THREAD_SYNC_LOCK, &sync_lock);
|
2017-10-19 09:59:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Exit from the sync point and unlock it if it was previously locked. If the
|
|
|
|
* current thread is the last one to have requested a sync, the sync pipe is
|
|
|
|
* flushed.
|
|
|
|
*/
|
|
|
|
void thread_exit_sync()
|
|
|
|
{
|
|
|
|
static volatile unsigned long barrier = 0;
|
|
|
|
|
2018-08-01 17:12:20 +00:00
|
|
|
if (!(all_threads_mask & (all_threads_mask - 1)))
|
2017-10-19 09:59:15 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (threads_want_sync & tid_bit)
|
2017-11-07 09:42:54 +00:00
|
|
|
HA_SPIN_UNLOCK(THREAD_SYNC_LOCK, &sync_lock);
|
2017-10-19 09:59:15 +00:00
|
|
|
|
|
|
|
if (HA_ATOMIC_AND(&threads_want_sync, ~tid_bit) == 0) {
|
|
|
|
char c;
|
|
|
|
|
|
|
|
shut_your_big_mouth_gcc(read(threads_sync_pipe[0], &c, 1));
|
|
|
|
fd_done_recv(threads_sync_pipe[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
thread_sync_barrier(&barrier);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-12 14:09:09 +00:00
|
|
|
__attribute__((constructor))
|
|
|
|
static void __hathreads_init(void)
|
|
|
|
{
|
2017-11-07 09:42:54 +00:00
|
|
|
HA_SPIN_INIT(&sync_lock);
|
2017-10-12 14:09:09 +00:00
|
|
|
#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
|
|
|
|
memset(lock_stats, 0, sizeof(lock_stats));
|
|
|
|
#endif
|
2017-11-05 10:50:18 +00:00
|
|
|
hap_register_build_opts("Built with multi-threading support.", 0);
|
2017-10-12 14:09:09 +00:00
|
|
|
}
|
|
|
|
|
2018-07-30 08:34:35 +00:00
|
|
|
#endif // USE_THREAD
|
|
|
|
|
|
|
|
|
|
|
|
/* Parse the number of threads in argument <arg>, returns it and adjusts a few
|
|
|
|
* internal variables accordingly, or fails and returns zero with an error
|
|
|
|
* reason in <errmsg>. May be called multiple times while parsing.
|
|
|
|
*/
|
|
|
|
int parse_nbthread(const char *arg, char **err)
|
|
|
|
{
|
|
|
|
long nbthread;
|
|
|
|
char *errptr;
|
|
|
|
|
|
|
|
nbthread = strtol(arg, &errptr, 10);
|
|
|
|
if (!*arg || *errptr) {
|
|
|
|
memprintf(err, "passed a missing or unparsable integer value in '%s'", arg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef USE_THREAD
|
|
|
|
if (nbthread != 1) {
|
|
|
|
memprintf(err, "specified with a value other than 1 while HAProxy is not compiled with threads support. Please check build options for USE_THREAD");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (nbthread < 1 || nbthread > MAX_THREADS) {
|
|
|
|
memprintf(err, "value must be between 1 and %d (was %ld)", MAX_THREADS, nbthread);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we proceed like this to be sure never to overflow the left shift */
|
|
|
|
all_threads_mask = 1UL << (nbthread - 1);
|
|
|
|
all_threads_mask |= all_threads_mask - 1;
|
2017-10-12 14:09:09 +00:00
|
|
|
#endif
|
2018-07-30 08:34:35 +00:00
|
|
|
return nbthread;
|
|
|
|
}
|