Remove unneeded signal in the cache-coherent case
[urcu.git] / urcu.c
1 /*
2 * urcu.c
3 *
4 * Userspace RCU library
5 *
6 * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
7 *
8 * Distributed under GPLv2
9 */
10
11 #include <stdio.h>
12 #include <pthread.h>
13 #include <signal.h>
14 #include <assert.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <poll.h>
19
20 #include "urcu.h"
21
22 pthread_mutex_t urcu_mutex = PTHREAD_MUTEX_INITIALIZER;
23
24 /*
25 * Global grace period counter.
26 * Contains the current RCU_GP_CTR_BIT.
27 * Also has a RCU_GP_CTR_BIT of 1, to accelerate the reader fast path.
28 * Written to only by writer with mutex taken. Read by both writer and readers.
29 */
30 long urcu_gp_ctr = RCU_GP_COUNT;
31
32 /*
33 * Written to only by each individual reader. Read by both the reader and the
34 * writers.
35 */
36 long __thread urcu_active_readers;
37
38 /* Thread IDs of registered readers */
39 #define INIT_NUM_THREADS 4
40
41 struct reader_registry {
42 pthread_t tid;
43 long *urcu_active_readers;
44 char *need_mb;
45 };
46
47 #ifdef DEBUG_YIELD
48 unsigned int yield_active;
49 unsigned int __thread rand_yield;
50 #endif
51
52 static struct reader_registry *registry;
53 static char __thread need_mb;
54 static int num_readers, alloc_readers;
55
56 void internal_urcu_lock(void)
57 {
58 int ret;
59
60 #ifndef DISTRUST_SIGNALS_EXTREME
61 ret = pthread_mutex_lock(&urcu_mutex);
62 if (ret) {
63 perror("Error in pthread mutex lock");
64 exit(-1);
65 }
66 #else /* #ifndef DISTRUST_SIGNALS_EXTREME */
67 while ((ret = pthread_mutex_trylock(&urcu_mutex)) != 0) {
68 if (ret != EBUSY && ret != EINTR) {
69 printf("ret = %d, errno = %d\n", ret, errno);
70 perror("Error in pthread mutex lock");
71 exit(-1);
72 }
73 if (need_mb) {
74 smp_mb();
75 need_mb = 0;
76 smp_mb();
77 }
78 poll(NULL,0,10);
79 }
80 #endif /* #else #ifndef DISTRUST_SIGNALS_EXTREME */
81 }
82
83 void internal_urcu_unlock(void)
84 {
85 int ret;
86
87 ret = pthread_mutex_unlock(&urcu_mutex);
88 if (ret) {
89 perror("Error in pthread mutex unlock");
90 exit(-1);
91 }
92 }
93
94 /*
95 * called with urcu_mutex held.
96 */
97 static void switch_next_urcu_qparity(void)
98 {
99 STORE_SHARED(urcu_gp_ctr, urcu_gp_ctr ^ RCU_GP_CTR_BIT);
100 }
101
102 #ifdef DEBUG_FULL_MB
103 #ifdef HAS_INCOHERENT_CACHES
104 static void force_mb_single_thread(struct reader_registry *index)
105 {
106 smp_mb();
107 }
108 #endif /* #ifdef HAS_INCOHERENT_CACHES */
109
110 static void force_mb_all_threads(void)
111 {
112 smp_mb();
113 }
114 #else /* #ifdef DEBUG_FULL_MB */
115 #ifdef HAS_INCOHERENT_CACHES
116 static void force_mb_single_thread(struct reader_registry *index)
117 {
118 assert(registry);
119 /*
120 * pthread_kill has a smp_mb(). But beware, we assume it performs
121 * a cache flush on architectures with non-coherent cache. Let's play
122 * safe and don't assume anything : we use smp_mc() to make sure the
123 * cache flush is enforced.
124 */
125 *index->need_mb = 1;
126 smp_mc(); /* write ->need_mb before sending the signals */
127 pthread_kill(index->tid, SIGURCU);
128 smp_mb();
129 /*
130 * Wait for sighandler (and thus mb()) to execute on every thread.
131 * BUSY-LOOP.
132 */
133 while (*index->need_mb) {
134 poll(NULL, 0, 1);
135 }
136 smp_mb(); /* read ->need_mb before ending the barrier */
137 }
138 #endif /* #ifdef HAS_INCOHERENT_CACHES */
139
140 static void force_mb_all_threads(void)
141 {
142 struct reader_registry *index;
143 /*
144 * Ask for each threads to execute a smp_mb() so we can consider the
145 * compiler barriers around rcu read lock as real memory barriers.
146 */
147 if (!registry)
148 return;
149 /*
150 * pthread_kill has a smp_mb(). But beware, we assume it performs
151 * a cache flush on architectures with non-coherent cache. Let's play
152 * safe and don't assume anything : we use smp_mc() to make sure the
153 * cache flush is enforced.
154 */
155 for (index = registry; index < registry + num_readers; index++) {
156 *index->need_mb = 1;
157 smp_mc(); /* write need_mb before sending the signal */
158 pthread_kill(index->tid, SIGURCU);
159 }
160 /*
161 * Wait for sighandler (and thus mb()) to execute on every thread.
162 *
163 * Note that the pthread_kill() will never be executed on systems
164 * that correctly deliver signals in a timely manner. However, it
165 * is not uncommon for kernels to have bugs that can result in
166 * lost or unduly delayed signals.
167 *
168 * If you are seeing the below pthread_kill() executing much at
169 * all, we suggest testing the underlying kernel and filing the
170 * relevant bug report. For Linux kernels, we recommend getting
171 * the Linux Test Project (LTP).
172 */
173 for (index = registry; index < registry + num_readers; index++) {
174 while (*index->need_mb) {
175 pthread_kill(index->tid, SIGURCU);
176 poll(NULL, 0, 1);
177 }
178 }
179 smp_mb(); /* read ->need_mb before ending the barrier */
180 }
181 #endif /* #else #ifdef DEBUG_FULL_MB */
182
183 void wait_for_quiescent_state(void)
184 {
185 struct reader_registry *index;
186
187 if (!registry)
188 return;
189 /*
190 * Wait for each thread urcu_active_readers count to become 0.
191 */
192 for (index = registry; index < registry + num_readers; index++) {
193 #ifndef HAS_INCOHERENT_CACHES
194 while (rcu_old_gp_ongoing(index->urcu_active_readers))
195 cpu_relax();
196 #else /* #ifndef HAS_INCOHERENT_CACHES */
197 int wait_loops = 0;
198 /*
199 * BUSY-LOOP. Force the reader thread to commit its
200 * urcu_active_readers update to memory if we wait for too long.
201 */
202 while (rcu_old_gp_ongoing(index->urcu_active_readers)) {
203 if (wait_loops++ == KICK_READER_LOOPS) {
204 force_mb_single_thread(index);
205 wait_loops = 0;
206 } else {
207 cpu_relax();
208 }
209 }
210 #endif /* #else #ifndef HAS_INCOHERENT_CACHES */
211 }
212 }
213
214 void synchronize_rcu(void)
215 {
216 internal_urcu_lock();
217
218 /* All threads should read qparity before accessing data structure
219 * where new ptr points to. Must be done within internal_urcu_lock
220 * because it iterates on reader threads.*/
221 /* Write new ptr before changing the qparity */
222 force_mb_all_threads();
223
224 switch_next_urcu_qparity(); /* 0 -> 1 */
225
226 /*
227 * Must commit qparity update to memory before waiting for parity
228 * 0 quiescent state. Failure to do so could result in the writer
229 * waiting forever while new readers are always accessing data (no
230 * progress).
231 * Ensured by STORE_SHARED and LOAD_SHARED.
232 */
233
234 /*
235 * Wait for previous parity to be empty of readers.
236 */
237 wait_for_quiescent_state(); /* Wait readers in parity 0 */
238
239 /*
240 * Must finish waiting for quiescent state for parity 0 before
241 * committing qparity update to memory. Failure to do so could result in
242 * the writer waiting forever while new readers are always accessing
243 * data (no progress).
244 * Ensured by STORE_SHARED and LOAD_SHARED.
245 */
246
247 switch_next_urcu_qparity(); /* 1 -> 0 */
248
249 /*
250 * Must commit qparity update to memory before waiting for parity
251 * 1 quiescent state. Failure to do so could result in the writer
252 * waiting forever while new readers are always accessing data (no
253 * progress).
254 * Ensured by STORE_SHARED and LOAD_SHARED.
255 */
256
257 /*
258 * Wait for previous parity to be empty of readers.
259 */
260 wait_for_quiescent_state(); /* Wait readers in parity 1 */
261
262 /* Finish waiting for reader threads before letting the old ptr being
263 * freed. Must be done within internal_urcu_lock because it iterates on
264 * reader threads. */
265 force_mb_all_threads();
266
267 internal_urcu_unlock();
268 }
269
270 void urcu_add_reader(pthread_t id)
271 {
272 struct reader_registry *oldarray;
273
274 if (!registry) {
275 alloc_readers = INIT_NUM_THREADS;
276 num_readers = 0;
277 registry =
278 malloc(sizeof(struct reader_registry) * alloc_readers);
279 }
280 if (alloc_readers < num_readers + 1) {
281 oldarray = registry;
282 registry = malloc(sizeof(struct reader_registry)
283 * (alloc_readers << 1));
284 memcpy(registry, oldarray,
285 sizeof(struct reader_registry) * alloc_readers);
286 alloc_readers <<= 1;
287 free(oldarray);
288 }
289 registry[num_readers].tid = id;
290 /* reference to the TLS of _this_ reader thread. */
291 registry[num_readers].urcu_active_readers = &urcu_active_readers;
292 registry[num_readers].need_mb = &need_mb;
293 num_readers++;
294 }
295
296 /*
297 * Never shrink (implementation limitation).
298 * This is O(nb threads). Eventually use a hash table.
299 */
300 void urcu_remove_reader(pthread_t id)
301 {
302 struct reader_registry *index;
303
304 assert(registry != NULL);
305 for (index = registry; index < registry + num_readers; index++) {
306 if (pthread_equal(index->tid, id)) {
307 memcpy(index, &registry[num_readers - 1],
308 sizeof(struct reader_registry));
309 registry[num_readers - 1].tid = 0;
310 registry[num_readers - 1].urcu_active_readers = NULL;
311 num_readers--;
312 return;
313 }
314 }
315 /* Hrm not found, forgot to register ? */
316 assert(0);
317 }
318
319 void urcu_register_thread(void)
320 {
321 internal_urcu_lock();
322 urcu_add_reader(pthread_self());
323 internal_urcu_unlock();
324 }
325
326 void urcu_unregister_thread(void)
327 {
328 internal_urcu_lock();
329 urcu_remove_reader(pthread_self());
330 internal_urcu_unlock();
331 }
332
333 #ifndef DEBUG_FULL_MB
334 void sigurcu_handler(int signo, siginfo_t *siginfo, void *context)
335 {
336 /*
337 * Executing this smp_mb() is the only purpose of this signal handler.
338 * It punctually promotes barrier() into smp_mb() on every thread it is
339 * executed on.
340 */
341 smp_mb();
342 need_mb = 0;
343 smp_mb();
344 }
345
346 void __attribute__((constructor)) urcu_init(void)
347 {
348 struct sigaction act;
349 int ret;
350
351 act.sa_sigaction = sigurcu_handler;
352 ret = sigaction(SIGURCU, &act, NULL);
353 if (ret) {
354 perror("Error in sigaction");
355 exit(-1);
356 }
357 }
358
359 void __attribute__((destructor)) urcu_exit(void)
360 {
361 struct sigaction act;
362 int ret;
363
364 ret = sigaction(SIGURCU, NULL, &act);
365 if (ret) {
366 perror("Error in sigaction");
367 exit(-1);
368 }
369 assert(act.sa_sigaction == sigurcu_handler);
370 free(registry);
371 }
372 #endif /* #ifndef DEBUG_FULL_MB */
This page took 0.037373 seconds and 5 git commands to generate.