1 | /* Test program for a read-phase / write-phase explicit hand-over. |
2 | Copyright (C) 2017-2022 Free Software Foundation, Inc. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public License as |
6 | published by the Free Software Foundation; either version 2.1 of the |
7 | License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; see the file COPYING.LIB. If |
16 | not, see <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <errno.h> |
19 | #include <error.h> |
20 | #include <pthread.h> |
21 | #include <stdio.h> |
22 | #include <stdlib.h> |
23 | #include <unistd.h> |
24 | #include <stdint.h> |
25 | #include <time.h> |
26 | #include <atomic.h> |
27 | #include <support/xthread.h> |
28 | |
29 | /* We realy want to set threads to 2 to reproduce this issue. The goal |
30 | is to have one primary writer and a single reader, and to hit the |
31 | bug that happens in the interleaving of those two phase transitions. |
32 | However, on most hardware, adding a second writer seems to help the |
33 | interleaving happen slightly more often, say 20% of the time. On a |
34 | 16 core ppc64 machine this fails 100% of the time with an unpatched |
35 | glibc. On a 8 core x86_64 machine this fails ~93% of the time, but |
36 | it doesn't fail at all on a 4 core system, so having available |
37 | unloaded cores makes a big difference in reproducibility. On an 8 |
38 | core qemu/kvm guest the reproducer reliability drops to ~10%. */ |
39 | #define THREADS 3 |
40 | |
41 | #define KIND PTHREAD_RWLOCK_PREFER_READER_NP |
42 | |
43 | static pthread_rwlock_t lock; |
44 | static int done = 0; |
45 | |
46 | static void* |
47 | tf (void* arg) |
48 | { |
49 | while (atomic_load_relaxed (&done) == 0) |
50 | { |
51 | int rcnt = 0; |
52 | int wcnt = 100; |
53 | if ((uintptr_t) arg == 0) |
54 | { |
55 | rcnt = 1; |
56 | wcnt = 1; |
57 | } |
58 | |
59 | do |
60 | { |
61 | if (wcnt) |
62 | { |
63 | xpthread_rwlock_wrlock (rwlock: &lock); |
64 | xpthread_rwlock_unlock (rwlock: &lock); |
65 | wcnt--; |
66 | } |
67 | if (rcnt) |
68 | { |
69 | xpthread_rwlock_rdlock (rwlock: &lock); |
70 | xpthread_rwlock_unlock (rwlock: &lock); |
71 | rcnt--; |
72 | } |
73 | } |
74 | while ((atomic_load_relaxed (&done) == 0) && (rcnt + wcnt > 0)); |
75 | |
76 | } |
77 | return NULL; |
78 | } |
79 | |
80 | |
81 | |
82 | static int |
83 | do_test (void) |
84 | { |
85 | pthread_t thr[THREADS]; |
86 | int n; |
87 | pthread_rwlockattr_t attr; |
88 | |
89 | xpthread_rwlockattr_init (attr: &attr); |
90 | xpthread_rwlockattr_setkind_np (attr: &attr, KIND); |
91 | |
92 | xpthread_rwlock_init (rwlock: &lock, attr: &attr); |
93 | |
94 | /* Make standard error the same as standard output. */ |
95 | dup2 (fd: 1, fd2: 2); |
96 | |
97 | /* Make sure we see all message, even those on stdout. */ |
98 | setvbuf (stdout, NULL, _IONBF, n: 0); |
99 | |
100 | for (n = 0; n < THREADS; ++n) |
101 | thr[n] = xpthread_create (NULL, thread_func: tf, closure: (void *) (uintptr_t) n); |
102 | |
103 | struct timespec delay; |
104 | delay.tv_sec = 10; |
105 | delay.tv_nsec = 0; |
106 | nanosleep (requested_time: &delay, NULL); |
107 | atomic_store_relaxed (&done, 1); |
108 | |
109 | /* Wait for all the threads. */ |
110 | for (n = 0; n < THREADS; ++n) |
111 | xpthread_join (thr: thr[n]); |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | #include <support/test-driver.c> |
117 | |