blob: 50f3c77011d90bf6e5d19742d470fa37c2782c4a [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);
51
52 fdtab[rfd].owner = NULL;
53 fdtab[rfd].iocb = thread_sync_io_handler;
Christopher Faulet36716a72017-05-30 11:07:16 +020054 fd_insert(rfd, MAX_THREADS_MASK);
Christopher Faulet339fff82017-10-19 11:59:15 +020055
56 all_threads_mask = mask;
57 return 0;
58}
59
60/* Enables the sync point. */
61void thread_sync_enable(void)
62{
63 fd_want_recv(threads_sync_pipe[0]);
64}
65
66/* Called when a thread want to pass into the sync point. It subscribes the
67 * current thread in threads waiting for sync by update a bit-field. It this is
68 * the first one, it wakeup all other threads by writing on the sync pipe.
69 */
70void thread_want_sync()
71{
72 if (all_threads_mask) {
Christopher Faulet81991d32017-12-02 09:53:24 +010073 if (threads_want_sync & tid_bit)
74 return;
Christopher Faulet339fff82017-10-19 11:59:15 +020075 if (HA_ATOMIC_OR(&threads_want_sync, tid_bit) == tid_bit)
76 shut_your_big_mouth_gcc(write(threads_sync_pipe[1], "S", 1));
77 }
78 else {
79 threads_want_sync = 1;
80 }
81}
82
83/* Returns 1 if no thread has requested a sync. Otherwise, it returns 0. */
84int thread_no_sync()
85{
86 return (threads_want_sync == 0);
87}
88
89/* Returns 1 if the current thread has requested a sync. Otherwise, it returns
90 * 0.
91 */
92int thread_need_sync()
93{
94 return (threads_want_sync & tid_bit);
95}
96
97/* Thread barrier. Synchronizes all threads at the barrier referenced by
98 * <barrier>. The calling thread shall block until all other threads have called
99 * thread_sync_barrier specifying the same barrier.
100 *
101 * If you need to use several barriers at differnt points, you need to use a
102 * different <barrier> for each point.
103 */
104static inline void thread_sync_barrier(volatile unsigned long *barrier)
105{
106 unsigned long old = all_threads_mask;
107
108 HA_ATOMIC_CAS(barrier, &old, 0);
Christopher Faulet209d02a2017-10-27 23:01:38 +0200109 HA_ATOMIC_OR(barrier, tid_bit);
Christopher Faulet339fff82017-10-19 11:59:15 +0200110 while (*barrier != all_threads_mask)
111 pl_cpu_relax();
112}
113
114/* Enter into the sync point and lock it if the current thread has requested a
115 * sync. */
116void thread_enter_sync()
117{
118 static volatile unsigned long barrier = 0;
119
120 if (!all_threads_mask)
121 return;
122
123 thread_sync_barrier(&barrier);
124 if (threads_want_sync & tid_bit)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100125 HA_SPIN_LOCK(THREAD_SYNC_LOCK, &sync_lock);
Christopher Faulet339fff82017-10-19 11:59:15 +0200126}
127
128/* Exit from the sync point and unlock it if it was previously locked. If the
129 * current thread is the last one to have requested a sync, the sync pipe is
130 * flushed.
131 */
132void thread_exit_sync()
133{
134 static volatile unsigned long barrier = 0;
135
136 if (!all_threads_mask)
137 return;
138
139 if (threads_want_sync & tid_bit)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100140 HA_SPIN_UNLOCK(THREAD_SYNC_LOCK, &sync_lock);
Christopher Faulet339fff82017-10-19 11:59:15 +0200141
142 if (HA_ATOMIC_AND(&threads_want_sync, ~tid_bit) == 0) {
143 char c;
144
145 shut_your_big_mouth_gcc(read(threads_sync_pipe[0], &c, 1));
146 fd_done_recv(threads_sync_pipe[0]);
147 }
148
149 thread_sync_barrier(&barrier);
150}
151
152
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200153__attribute__((constructor))
154static void __hathreads_init(void)
155{
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100156 HA_SPIN_INIT(&sync_lock);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200157#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
158 memset(lock_stats, 0, sizeof(lock_stats));
159#endif
Willy Tarreau6dbd3e92017-11-05 11:50:18 +0100160 hap_register_build_opts("Built with multi-threading support.", 0);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200161}
162
163#endif