blob: eb9cd3fcecdf6df42938d81583fbc994a6198343 [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) {
73 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{
84 return (threads_want_sync == 0);
85}
86
87/* Returns 1 if the current thread has requested a sync. Otherwise, it returns
88 * 0.
89 */
90int thread_need_sync()
91{
92 return (threads_want_sync & tid_bit);
93}
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);
Christopher Faulet339fff82017-10-19 11:59:15 +0200108 while (*barrier != all_threads_mask)
109 pl_cpu_relax();
110}
111
112/* Enter into the sync point and lock it if the current thread has requested a
113 * sync. */
114void thread_enter_sync()
115{
116 static volatile unsigned long barrier = 0;
117
118 if (!all_threads_mask)
119 return;
120
121 thread_sync_barrier(&barrier);
122 if (threads_want_sync & tid_bit)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100123 HA_SPIN_LOCK(THREAD_SYNC_LOCK, &sync_lock);
Christopher Faulet339fff82017-10-19 11:59:15 +0200124}
125
126/* Exit from the sync point and unlock it if it was previously locked. If the
127 * current thread is the last one to have requested a sync, the sync pipe is
128 * flushed.
129 */
130void thread_exit_sync()
131{
132 static volatile unsigned long barrier = 0;
133
134 if (!all_threads_mask)
135 return;
136
137 if (threads_want_sync & tid_bit)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100138 HA_SPIN_UNLOCK(THREAD_SYNC_LOCK, &sync_lock);
Christopher Faulet339fff82017-10-19 11:59:15 +0200139
140 if (HA_ATOMIC_AND(&threads_want_sync, ~tid_bit) == 0) {
141 char c;
142
143 shut_your_big_mouth_gcc(read(threads_sync_pipe[0], &c, 1));
144 fd_done_recv(threads_sync_pipe[0]);
145 }
146
147 thread_sync_barrier(&barrier);
148}
149
150
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200151__attribute__((constructor))
152static void __hathreads_init(void)
153{
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100154 HA_SPIN_INIT(&sync_lock);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200155#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
156 memset(lock_stats, 0, sizeof(lock_stats));
157#endif
Willy Tarreau6dbd3e92017-11-05 11:50:18 +0100158 hap_register_build_opts("Built with multi-threading support.", 0);
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200159}
160
161#endif