8e3cae34c8995eced21b6f3010645a9b0e1b34f6
[sfrench/samba-autobuild/.git] / ctdb / tests / src / test_mutex_raw.c
1 /*
2    Robust mutex test
3
4    Copyright (C) Amitay Isaacs  2016
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21  * Run this test as follows:
22  *
23  * 1. Running all processes at normal priority
24  *
25  *  $ while true ; do ./bin/test_mutex_raw /tmp/foo 10 0 ; done
26  *
27  * 2. Running all processes at real-time priority
28  *
29  *  # while true ; do ./bin/test_mutex_raw /tmp/foo 10 1 ; done
30  *
31  * The test will block after few iterations.  At this time none of the 
32  * child processes is holding the mutex.
33  *
34  * To check which process is holding a lock:
35  *
36  *  $ ./bin/test_mutex_raw /tmp/foo debug
37  *
38  *  If no pid is printed, then no process is holding the mutex.
39  */
40
41 #include <stdio.h>
42 #include <unistd.h>
43 #include <inttypes.h>
44 #include <sys/types.h>
45 #include <sys/fcntl.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <sys/wait.h>
49 #include <sched.h>
50 #include <sys/mman.h>
51 #include <pthread.h>
52 #include <errno.h>
53 #include <stdbool.h>
54
55 int pthread_mutex_consistent_np(pthread_mutex_t *);
56
57 static void set_realtime(void)
58 {
59         struct sched_param p;
60         int ret;
61
62         p.sched_priority = 1;
63
64         ret = sched_setscheduler(0, SCHED_FIFO, &p);
65         if (ret == -1) {
66                 fprintf(stderr, "Failed to set scheduler to SCHED_FIFO\n");
67         }
68 }
69
70 static void high_priority(void)
71 {
72         int ret;
73
74         ret = nice(-20);
75         if (ret == -1) {
76                 fprintf(stderr, "Failed to set high priority\n");
77         }
78 }
79
80 static void run_child(const char *filename)
81 {
82         pthread_mutex_t *mutex;
83         void *addr;
84         int ret, fd;
85
86         fd = open(filename, O_RDWR, 0600);
87         if (fd == -1) {
88                 exit(1);
89         }
90
91         addr = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ|PROT_WRITE,
92                     MAP_SHARED|MAP_FILE, fd, 0);
93         if (addr == NULL) {
94                 exit(2);
95         }
96
97         mutex = (pthread_mutex_t *)addr;
98
99 again:
100         ret = pthread_mutex_lock(mutex);
101         if (ret == EOWNERDEAD) {
102                 ret = pthread_mutex_consistent_np(mutex);
103         } else if (ret == EAGAIN) {
104                 goto again;
105         }
106         if (ret != 0) {
107                 fprintf(stderr, "pid %u lock failed, ret=%d\n", getpid(), ret);
108                 exit(3);
109         }
110
111         fprintf(stderr, "pid %u locked\n", getpid());
112         kill(getpid(), SIGKILL);
113 }
114
115 #define PRIO_NORMAL     0
116 #define PRIO_REALTIME   1
117 #define PRIO_NICE_20    2
118
119 int main(int argc, const char **argv)
120 {
121         pthread_mutexattr_t ma;
122         pthread_mutex_t *mutex;
123         int fd, ret, i;
124         pid_t pid;
125         void *addr;
126         int num_children;
127         int priority = PRIO_NORMAL;
128
129         if (argc < 3 || argc > 4) {
130                 fprintf(stderr, "Usage: %s <file> <n> [0|1|2]\n", argv[0]);
131                 fprintf(stderr, "       %s <file> debug\n", argv[0]);
132                 exit(1);
133         }
134
135         if (argc == 4) {
136                 priority = atoi(argv[3]);
137         }
138
139         if (priority == PRIO_REALTIME) {
140                 set_realtime();
141         } else if (priority == PRIO_NICE_20) {
142                 high_priority();
143         }
144
145         fd = open(argv[1], O_CREAT|O_RDWR, 0600);
146         if (fd == -1) {
147                 fprintf(stderr, "open failed\n");
148                 exit(1);
149         }
150
151         ret = lseek(fd, 0, SEEK_SET);
152         if (ret != 0) {
153                 fprintf(stderr, "lseek failed\n");
154                 exit(1);
155         }
156
157         ret = ftruncate(fd, sizeof(pthread_mutex_t));
158         if (ret != 0) {
159                 fprintf(stderr, "ftruncate failed\n");
160                 exit(1);
161         }
162
163         addr = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ|PROT_WRITE,
164                     MAP_SHARED|MAP_FILE, fd, 0);
165         if (addr == NULL) {
166                 fprintf(stderr, "mmap failed\n");
167                 exit(1);
168         }
169
170         mutex = (pthread_mutex_t *)addr;
171
172         if (strcmp(argv[2], "debug") == 0) {
173                 ret = pthread_mutex_trylock(mutex);
174                 if (ret == EOWNERDEAD) {
175                         ret = pthread_mutex_consistent_np(mutex);
176                         if (ret == 0) {
177                                 pthread_mutex_unlock(mutex);
178                         }
179                 } else if (ret == EBUSY) {
180                         printf("pid=%u\n", mutex->__data.__owner);
181                 } else if (ret == 0) {
182                         pthread_mutex_unlock(mutex);
183                 }
184                 exit(0);
185         }
186
187         ret = pthread_mutexattr_init(&ma);
188         if (ret != 0) {
189                 fprintf(stderr, "pthread_mutexattr_init failed\n");
190                 exit(1);
191         }
192
193         ret = pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK);
194         if (ret != 0) {
195                 fprintf(stderr, "pthread_mutexattr_settype failed\n");
196                 exit(1);
197         }
198
199         ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED);
200         if (ret != 0) {
201                 fprintf(stderr, "pthread_mutexattr_setpshared failed\n");
202                 exit(1);
203         }
204
205         ret = pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);
206         if (ret != 0) {
207                 fprintf(stderr, "pthread_mutexattr_setrobust failed\n");
208                 exit(1);
209         }
210
211         ret = pthread_mutex_init(mutex, &ma);
212         if (ret != 0) {
213                 fprintf(stderr, "pthread_mutex_init failed\n");
214                 exit(1);
215         }
216
217         ret = pthread_mutex_lock(mutex);
218         if (ret != 0) {
219                 fprintf(stderr, "pthread_mutex_lock failed\n");
220                 exit(1);
221         }
222
223         setpgid(0, 0);
224
225         fprintf(stderr, "Creating children\n");
226         num_children = atoi(argv[2]);
227
228         for (i=0; i<num_children; i++) {
229                 pid = fork();
230                 if (pid < 0) {
231                         fprintf(stderr, "fork() failed\n");
232                         exit(1);
233                 }
234                 if (pid == 0) {
235                         close(fd);
236                         run_child(argv[1]);
237                         exit(1);
238                 }
239         }
240
241         fprintf(stderr, "Waiting for children\n");
242
243         ret = pthread_mutex_unlock(mutex);
244         if (ret != 0) {
245                 fprintf(stderr, "pthread_mutex_unlock failed\n");
246                 exit(1);
247         }
248
249         for (i=0; i<num_children; i++) {
250                 int status;
251
252                 pid = waitpid(-1, &status, 0);
253                 if (pid <= 0) {
254                         fprintf(stderr, "waitpid() failed\n");
255                 }
256         }
257
258         close(fd);
259         unlink(argv[1]);
260         exit(0);
261 }