blob: 26a6493099ad0a983ecedc23a716b12b838d179a [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
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020016#include <common/hathreads.h>
Christopher Faulet339fff82017-10-19 11:59:15 +020017#include <common/standard.h>
18#include <proto/fd.h>
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020019
20THREAD_LOCAL unsigned int tid = 0;
21THREAD_LOCAL unsigned int tid_bit = (1UL << 0);
22
23#ifdef USE_THREAD
24
Christopher Faulet339fff82017-10-19 11:59:15 +020025static HA_SPINLOCK_T sync_lock;
26static int threads_sync_pipe[2];
27static unsigned long threads_want_sync = 0;
28static unsigned long all_threads_mask = 0;
29
Christopher Faulet1a2b56e2017-10-12 16:09:09 +020030#if defined(DEBUG_THREAD) || defined(DEBUG_FULL)
31struct lock_stat lock_stats[LOCK_LABELS];
32#endif
33
Christopher Faulet339fff82017-10-19 11:59:15 +020034/* Dummy I/O handler used by the sync pipe.*/
35static void thread_sync_io_handler(int fd) { }
36
37/* Initializes the sync point. It creates a pipe used by threads to wakup all
38 * others when a sync is requested. It also initialize the mask of all create
39 * threads. It returns 0 on success and -1 if an error occurred.
40 */
41int thread_sync_init(unsigned long mask)
42{
43 int rfd;
44
45 if (pipe(threads_sync_pipe) < 0)
46 return -1;
47
48 rfd = threads_sync_pipe[0];
49 fcntl(rfd, F_SETFL, O_NONBLOCK);
50
51 fdtab[rfd].owner = NULL;
52 fdtab[rfd].iocb = thread_sync_io_handler;
Christopher Faulet36716a72017-05-30 11:07:16 +020053 fd_insert(rfd, 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) {
72 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)
122 SPIN_LOCK(THREAD_SYNC_LOCK, &sync_lock);
123}
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)
137 SPIN_UNLOCK(THREAD_SYNC_LOCK, &sync_lock);
138
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 Faulet339fff82017-10-19 11:59:15 +0200153 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
Christopher Faulet1a2b56e2017-10-12 16:09:09 +0200157}
158
159#endif