blob: 839b08696bb9a82133343f6822f5f60c6a23c07a [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>
14#include <fcntl.h>
15
Willy Tarreau04931492017-11-03 23:39:25 +010016#include <common/cfgparse.h>
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020017#include <common/hathreads.h>
Christopher Faulet339fff82017-10-19 11:59:15 +020018#include <common/standard.h>
19#include <proto/fd.h>
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020020
Christopher Faulete9a896e2017-11-14 10:16:04 +010021THREAD_LOCAL unsigned int tid = 0;
22THREAD_LOCAL unsigned long tid_bit = (1UL << 0);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020023
Willy Tarreaue96e61c2018-03-29 18:54:33 +020024/* Dummy I/O handler used by the sync pipe.*/
25void thread_sync_io_handler(int fd)
26{
27}
28
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020029#ifdef USE_THREAD
30
Christopher Faulet339fff82017-10-19 11:59:15 +020031static HA_SPINLOCK_T sync_lock;
32static int threads_sync_pipe[2];
33static unsigned long threads_want_sync = 0;
34static unsigned long all_threads_mask = 0;
35
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020036#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
37struct lock_stat lock_stats[LOCK_LABELS];
38#endif
39
Christopher Faulet339fff82017-10-19 11:59:15 +020040/* Initializes the sync point. It creates a pipe used by threads to wakup all
41 * others when a sync is requested. It also initialize the mask of all create
42 * threads. It returns 0 on success and -1 if an error occurred.
43 */
44int thread_sync_init(unsigned long mask)
45{
46 int rfd;
47
48 if (pipe(threads_sync_pipe) < 0)
49 return -1;
50
51 rfd = threads_sync_pipe[0];
52 fcntl(rfd, F_SETFL, O_NONBLOCK);
Willy Tarreaua9786b62018-01-25 07:22:13 +010053 fd_insert(rfd, thread_sync_io_handler, thread_sync_io_handler, MAX_THREADS_MASK);
Christopher Faulet339fff82017-10-19 11:59:15 +020054
55 all_threads_mask = mask;
56 return 0;
57}
58
59/* Enables the sync point. */
60void thread_sync_enable(void)
61{
62 fd_want_recv(threads_sync_pipe[0]);
63}
64
65/* Called when a thread want to pass into the sync point. It subscribes the
66 * current thread in threads waiting for sync by update a bit-field. It this is
67 * the first one, it wakeup all other threads by writing on the sync pipe.
68 */
69void thread_want_sync()
70{
71 if (all_threads_mask) {
Christopher Faulet81991d32017-12-02 09:53:24 +010072 if (threads_want_sync & tid_bit)
73 return;
Christopher Faulet339fff82017-10-19 11:59:15 +020074 if (HA_ATOMIC_OR(&threads_want_sync, tid_bit) == tid_bit)
75 shut_your_big_mouth_gcc(write(threads_sync_pipe[1], "S", 1));
76 }
77 else {
78 threads_want_sync = 1;
79 }
80}
81
82/* Returns 1 if no thread has requested a sync. Otherwise, it returns 0. */
83int thread_no_sync()
84{
85 return (threads_want_sync == 0);
86}
87
88/* Returns 1 if the current thread has requested a sync. Otherwise, it returns
89 * 0.
90 */
91int thread_need_sync()
92{
93 return (threads_want_sync & tid_bit);
94}
95
96/* Thread barrier. Synchronizes all threads at the barrier referenced by
97 * <barrier>. The calling thread shall block until all other threads have called
98 * thread_sync_barrier specifying the same barrier.
99 *
100 * If you need to use several barriers at differnt points, you need to use a
101 * different <barrier> for each point.
102 */
103static inline void thread_sync_barrier(volatile unsigned long *barrier)
104{
105 unsigned long old = all_threads_mask;
106
107 HA_ATOMIC_CAS(barrier, &old, 0);
Christopher Faulet209d02a2017-10-27 23:01:38 +0200108 HA_ATOMIC_OR(barrier, tid_bit);
Christopher Faulet339fff82017-10-19 11:59:15 +0200109 while (*barrier != all_threads_mask)
110 pl_cpu_relax();
111}
112
113/* Enter into the sync point and lock it if the current thread has requested a
114 * sync. */
115void thread_enter_sync()
116{
117 static volatile unsigned long barrier = 0;
118
119 if (!all_threads_mask)
120 return;
121
122 thread_sync_barrier(&barrier);
123 if (threads_want_sync & tid_bit)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100124 HA_SPIN_LOCK(THREAD_SYNC_LOCK, &sync_lock);
Christopher Faulet339fff82017-10-19 11:59:15 +0200125}
126
127/* Exit from the sync point and unlock it if it was previously locked. If the
128 * current thread is the last one to have requested a sync, the sync pipe is
129 * flushed.
130 */
131void thread_exit_sync()
132{
133 static volatile unsigned long barrier = 0;
134
135 if (!all_threads_mask)
136 return;
137
138 if (threads_want_sync & tid_bit)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100139 HA_SPIN_UNLOCK(THREAD_SYNC_LOCK, &sync_lock);
Christopher Faulet339fff82017-10-19 11:59:15 +0200140
141 if (HA_ATOMIC_AND(&threads_want_sync, ~tid_bit) == 0) {
142 char c;
143
144 shut_your_big_mouth_gcc(read(threads_sync_pipe[0], &c, 1));
145 fd_done_recv(threads_sync_pipe[0]);
146 }
147
148 thread_sync_barrier(&barrier);
149}
150
151
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200152__attribute__((constructor))
153static void __hathreads_init(void)
154{
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100155 HA_SPIN_INIT(&sync_lock);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200156#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
157 memset(lock_stats, 0, sizeof(lock_stats));
158#endif
Willy Tarreau6dbd3e92017-11-05 11:50:18 +0100159 hap_register_build_opts("Built with multi-threading support.", 0);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200160}
161
162#endif