blob: fd0eb68464721f2997170502112345b9bb5fbb7c [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
24#ifdef USE_THREAD
25
Christopher Faulet339fff82017-10-19 11:59:15 +020026static HA_SPINLOCK_T sync_lock;
27static int threads_sync_pipe[2];
28static unsigned long threads_want_sync = 0;
29static unsigned long all_threads_mask = 0;
30
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020031#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
32struct lock_stat lock_stats[LOCK_LABELS];
33#endif
34
Christopher Faulet339fff82017-10-19 11:59:15 +020035/* Dummy I/O handler used by the sync pipe.*/
36static void thread_sync_io_handler(int fd) { }
37
38/* Initializes the sync point. It creates a pipe used by threads to wakup all
39 * others when a sync is requested. It also initialize the mask of all create
40 * threads. It returns 0 on success and -1 if an error occurred.
41 */
42int thread_sync_init(unsigned long mask)
43{
44 int rfd;
45
46 if (pipe(threads_sync_pipe) < 0)
47 return -1;
48
49 rfd = threads_sync_pipe[0];
50 fcntl(rfd, F_SETFL, O_NONBLOCK);
Willy Tarreaua9786b62018-01-25 07:22:13 +010051 fd_insert(rfd, thread_sync_io_handler, thread_sync_io_handler, MAX_THREADS_MASK);
Christopher Faulet339fff82017-10-19 11:59:15 +020052
53 all_threads_mask = mask;
54 return 0;
55}
56
57/* Enables the sync point. */
58void thread_sync_enable(void)
59{
60 fd_want_recv(threads_sync_pipe[0]);
61}
62
63/* Called when a thread want to pass into the sync point. It subscribes the
64 * current thread in threads waiting for sync by update a bit-field. It this is
65 * the first one, it wakeup all other threads by writing on the sync pipe.
66 */
67void thread_want_sync()
68{
69 if (all_threads_mask) {
Christopher Faulet81991d32017-12-02 09:53:24 +010070 if (threads_want_sync & tid_bit)
71 return;
Christopher Faulet339fff82017-10-19 11:59:15 +020072 if (HA_ATOMIC_OR(&threads_want_sync, tid_bit) == tid_bit)
73 shut_your_big_mouth_gcc(write(threads_sync_pipe[1], "S", 1));
74 }
75 else {
76 threads_want_sync = 1;
77 }
78}
79
80/* Returns 1 if no thread has requested a sync. Otherwise, it returns 0. */
81int thread_no_sync()
82{
83 return (threads_want_sync == 0);
84}
85
86/* Returns 1 if the current thread has requested a sync. Otherwise, it returns
87 * 0.
88 */
89int thread_need_sync()
90{
91 return (threads_want_sync & tid_bit);
92}
93
94/* Thread barrier. Synchronizes all threads at the barrier referenced by
95 * <barrier>. The calling thread shall block until all other threads have called
96 * thread_sync_barrier specifying the same barrier.
97 *
98 * If you need to use several barriers at differnt points, you need to use a
99 * different <barrier> for each point.
100 */
101static inline void thread_sync_barrier(volatile unsigned long *barrier)
102{
103 unsigned long old = all_threads_mask;
104
105 HA_ATOMIC_CAS(barrier, &old, 0);
Christopher Faulet209d02a2017-10-27 23:01:38 +0200106 HA_ATOMIC_OR(barrier, tid_bit);
Christopher Faulet339fff82017-10-19 11:59:15 +0200107 while (*barrier != all_threads_mask)
108 pl_cpu_relax();
109}
110
111/* Enter into the sync point and lock it if the current thread has requested a
112 * sync. */
113void thread_enter_sync()
114{
115 static volatile unsigned long barrier = 0;
116
117 if (!all_threads_mask)
118 return;
119
120 thread_sync_barrier(&barrier);
121 if (threads_want_sync & tid_bit)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100122 HA_SPIN_LOCK(THREAD_SYNC_LOCK, &sync_lock);
Christopher Faulet339fff82017-10-19 11:59:15 +0200123}
124
125/* Exit from the sync point and unlock it if it was previously locked. If the
126 * current thread is the last one to have requested a sync, the sync pipe is
127 * flushed.
128 */
129void thread_exit_sync()
130{
131 static volatile unsigned long barrier = 0;
132
133 if (!all_threads_mask)
134 return;
135
136 if (threads_want_sync & tid_bit)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100137 HA_SPIN_UNLOCK(THREAD_SYNC_LOCK, &sync_lock);
Christopher Faulet339fff82017-10-19 11:59:15 +0200138
139 if (HA_ATOMIC_AND(&threads_want_sync, ~tid_bit) == 0) {
140 char c;
141
142 shut_your_big_mouth_gcc(read(threads_sync_pipe[0], &c, 1));
143 fd_done_recv(threads_sync_pipe[0]);
144 }
145
146 thread_sync_barrier(&barrier);
147}
148
149
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200150__attribute__((constructor))
151static void __hathreads_init(void)
152{
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100153 HA_SPIN_INIT(&sync_lock);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200154#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
155 memset(lock_stats, 0, sizeof(lock_stats));
156#endif
Willy Tarreau6dbd3e92017-11-05 11:50:18 +0100157 hap_register_build_opts("Built with multi-threading support.", 0);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200158}
159
160#endif