lease02: Do a lock-unlock sequence instead of lock-lock
[jlayton/lockperf.git] / posix03.c
1 /*
2  * Copyright Rusty Russell, 2013, IBM Corporation
3  * Copyright Jeff Layton, 2013
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 3 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, see
17  * <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif /* HAVE_CONFIG_H */
23
24 #include <err.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <regex.h>
30 #include <assert.h>
31 #include <ctype.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <stdbool.h>
35 #include <sys/time.h>
36 #include <sys/resource.h>
37 #include <signal.h>
38 #include <sys/wait.h>
39
40 #include "timespec.h"
41
42 #define DEFAULT_PROCESSES 5000
43 #define DEFAULT_ITERATIONS 100
44
45 /* Taken from TDB: LGPLv3 */
46 static int fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag)
47 {
48         struct flock fl;
49
50         fl.l_type = rw;
51         fl.l_whence = SEEK_SET;
52         fl.l_start = off;
53         fl.l_len = len;
54         fl.l_pid = 0;
55
56         if (waitflag)
57                 return fcntl(fd, F_SETLKW, &fl);
58         else
59                 return fcntl(fd, F_SETLK, &fl);
60 }
61
62 static int fcntl_unlock(int fd, off_t off, off_t len)
63 {
64         struct flock fl;
65         fl.l_type = F_UNLCK;
66         fl.l_whence = SEEK_SET;
67         fl.l_start = off;
68         fl.l_len = len;
69         fl.l_pid = 0;
70
71         return fcntl(fd, F_SETLKW, &fl);
72 }
73
74 static void
75 kill_children()
76 {
77         siginfo_t       infop;
78
79         signal(SIGINT, SIG_IGN);
80         kill(0, SIGINT);
81         while (waitid(P_ALL, 0, &infop, WEXITED) != -1);
82 }
83
84 static void
85 sighandler(int sig __attribute__((unused)))
86 {
87         kill_children();
88         exit(0);
89 }
90
91 static int do_child(int lockfd, int i, int to_lockers, int from_lockers)
92 {
93         unsigned char c;
94
95         while (read(to_lockers, &c, 1) == 1) {
96                 if (c != '\0')
97                         return 0;
98                 if (fcntl_lock(lockfd, F_WRLCK, i, 1, true) != 0)
99                         err(1, "Locking");
100                 /* Tell parent we got it! */
101                 if (write(from_lockers, &c, 1) != 1)
102                         err(1, "Writing to parent");
103                 usleep(1000);
104                 fcntl_unlock(lockfd, i, 1);
105         }
106         return 0;
107 }
108
109 static int
110 usage(char *argv0)
111 {
112         errx(1, "Usage: %s [-i iterations] [-n nr_children] <filename>", argv0);
113 }
114
115 int main(int argc, char *argv[])
116 {
117         int num = DEFAULT_PROCESSES, i, opt;
118         int iter = DEFAULT_ITERATIONS;
119         const char *lockfile;
120         char *fill;
121         int lockfd, to_lockers[2], from_lockers[2];
122         struct timespec start, end;
123         struct timespec total;
124         struct rlimit rlim;
125
126         total.tv_sec = 0;
127         total.tv_nsec = 0;
128
129         while ((opt = getopt(argc, argv, "i:n:")) != -1) {
130                 switch (opt) {
131                 case 'i':
132                         iter = atoi(optarg);
133                         break;
134                 case 'n':
135                         num = atoi(optarg);
136                         break;
137                 default:
138                         usage(argv[0]);
139                 }
140         }
141
142         lockfile = argv[optind];
143         if (!lockfile)
144                 usage(argv[0]);
145
146         /* bump rlimit */
147         if (getrlimit(RLIMIT_NPROC, &rlim))
148                 err(1, "getrlimit");
149         rlim.rlim_cur = rlim.rlim_max;
150         if (setrlimit(RLIMIT_NPROC, &rlim))
151                 err(1, "setrlimit");
152
153         if (pipe(to_lockers))
154                 err(1, "pipe (to_lockers)");
155         if (pipe(from_lockers))
156                 err(1, "pipe (from_lockers)");
157
158         lockfd = open(lockfile, O_CREAT|O_RDWR, 0644);
159         if (lockfd < 0)
160                 err(1, "Opening %s", lockfile);
161
162         signal(SIGINT, sighandler);
163
164         for (i = 0; i < num; i++) {
165                 switch (fork()) {
166                 case 0:
167                         signal(SIGINT, SIG_DFL);
168                         return do_child(lockfd, i, to_lockers[0], from_lockers[1]);
169                 case -1:
170                         err(1, "fork failed");
171                 }
172         }
173
174         close(to_lockers[0]);
175         close(from_lockers[1]);
176
177         fill = calloc(num, 1);
178
179         while (iter--) {
180                 if (fcntl_lock(lockfd, F_WRLCK, 0, num, true) != 0)
181                         err(1, "Locking %u bytes in %s", num, lockfile);
182
183                 /* OK, now wake all the kids: they block on lock. */
184                 i = 0;
185                 do {
186                         int ret = write(to_lockers[1], fill + i, num - i);
187
188                         if (ret < 0)
189                                 err(1, "writing to wake up locker children");
190                         i += ret;
191                 } while (i < num);
192
193                 usleep(1000);
194
195                 if (clock_gettime(CLOCK_MONOTONIC_RAW, &start))
196                         err(1, "Getting start time");
197
198                 fcntl_unlock(lockfd, 0, num);
199                 for (i = 0; i < num; i++)
200                         if (read(from_lockers[0], fill, 1) != 1)
201                                 err(1, "Reading from locker children");
202
203                 if (clock_gettime(CLOCK_MONOTONIC_RAW, &end))
204                         err(1, "Getting start time");
205
206                 total = timespec_add(total, timespec_sub(end, start));
207         }
208
209         printf("%ld.%09ld\n", total.tv_sec, total.tv_nsec);
210         kill_children();
211         return 0;
212 }