posix03: add new "posix03" test
authorJeff Layton <jlayton@redhat.com>
Mon, 9 Sep 2013 19:13:57 +0000 (15:13 -0400)
committerJeff Layton <jlayton@redhat.com>
Mon, 9 Sep 2013 19:13:57 +0000 (15:13 -0400)
...we start with the original reproducer program written by Rusty.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
posix03.c [new file with mode: 0644]

diff --git a/posix03.c b/posix03.c
new file mode 100644 (file)
index 0000000..46bca96
--- /dev/null
+++ b/posix03.c
@@ -0,0 +1,188 @@
+/* Copyright Rusty Russell 2013, IBM Corporation
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see
+   <http://www.gnu.org/licenses/>. */
+
+#define HAVE_CLOCK_GETTIME_IN_LIBRT 1
+#define HAVE_STRUCT_TIMESPEC 1
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <assert.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+
+/* Taken from ccan/time: Licensed under BSD-MIT - see LICENSE file for details */
+#if HAVE_STRUCT_TIMESPEC
+#include <time.h>
+#else
+struct timespec {
+       time_t   tv_sec;        /* seconds */
+       long     tv_nsec;       /* nanoseconds */
+};
+#endif
+
+#if !HAVE_CLOCK_GETTIME && !HAVE_CLOCK_GETTIME_IN_LIBRT
+#include <sys/time.h>
+
+struct timespec time_now(void)
+{
+       struct timeval now;
+       struct timespec ret;
+       gettimeofday(&now, NULL);
+       ret.tv_sec = now.tv_sec;
+       ret.tv_nsec = now.tv_usec * 1000;
+       return ret;
+}
+#else
+#include <time.h>
+struct timespec time_now(void)
+{
+       struct timespec ret;
+       clock_gettime(CLOCK_REALTIME, &ret);
+       return ret;
+}
+#endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GETTIME_IN_LIBRT */
+
+static struct timespec time_sub(struct timespec recent,
+                                      struct timespec old)
+{
+       struct timespec diff;
+
+       diff.tv_sec = recent.tv_sec - old.tv_sec;
+       if (old.tv_nsec > recent.tv_nsec) {
+               diff.tv_sec--;
+               diff.tv_nsec = 1000000000 + recent.tv_nsec - old.tv_nsec;
+       } else
+               diff.tv_nsec = recent.tv_nsec - old.tv_nsec;
+
+       return diff;
+}
+
+/* Taken from TDB: LGPLv3 */
+static int fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag)
+{
+       struct flock fl;
+
+       fl.l_type = rw;
+       fl.l_whence = SEEK_SET;
+       fl.l_start = off;
+       fl.l_len = len;
+       fl.l_pid = 0;
+
+       if (waitflag)
+               return fcntl(fd, F_SETLKW, &fl);
+       else
+               return fcntl(fd, F_SETLK, &fl);
+}
+
+static int fcntl_unlock(int fd, int rw, off_t off, off_t len)
+{
+       struct flock fl;
+       fl.l_type = F_UNLCK;
+       fl.l_whence = SEEK_SET;
+       fl.l_start = off;
+       fl.l_len = len;
+       fl.l_pid = 0;
+
+       return fcntl(fd, F_SETLKW, &fl);
+}
+
+int main(int argc, char *argv[])
+{
+       int num = 5000, i;
+       const char *lockfile = argv[1];
+       char *fill;
+       int lockfd, to_lockers[2], from_lockers[2];
+       struct timespec start, diff;
+
+       if (argc > 2) {
+               num = atoi(argv[2]);
+               argv++;
+               argc--;
+       }
+       if (argc != 2)
+               errx(1, "Usage: lock-stampede <filename> [num]");
+
+       pipe(to_lockers);
+       pipe(from_lockers);
+       lockfd = open(lockfile, O_RDWR);
+       if (lockfd < 0)
+               err(1, "Opening %s", lockfile);
+
+       start = time_now();
+       for (i = 0; i < num; i++) {
+               char c;
+
+               switch (fork()) {
+               case 0:
+                       close(from_lockers[0]);
+                       close(to_lockers[1]);
+                       /* Parent dies? */
+                       if (read(to_lockers[0], &c, 1) != 1)
+                               exit(0);
+                       if (fcntl_lock(lockfd, F_WRLCK, i, 1, true) != 0)
+                               err(1, "Locking %s", lockfile);
+                       /* Tell parent we got it! */
+                       if (write(from_lockers[1], &c, 1) != 1)
+                               err(1, "Writing to parent");
+                       /* If we exit, we'd release it, so wait. */
+                       read(to_lockers[0], &c, 1);
+                       exit(0);
+               case -1:
+                       err(1, "fork failed");
+               }
+       }
+
+       close(to_lockers[0]);
+       close(from_lockers[1]);
+
+       diff = time_sub(time_now(), start);
+       printf("Children set up in %lu.%09u\n",
+              (long)diff.tv_sec, (int)diff.tv_nsec);
+
+       if (fcntl_lock(lockfd, F_WRLCK, 0, num, true) != 0)
+               err(1, "Locking %u bytes in %s", num, lockfile);
+
+       fill = calloc(num, 1);
+
+       /* OK, now wake all the kids: they block on lock. */
+       i = 0;
+       do {
+               int ret = write(to_lockers[1], fill + i, num - i);
+
+               if (ret < 0)
+                       err(1, "writing to wake up locker children");
+               i += ret;
+       } while (i < num);
+
+       sleep(1);
+
+       start = time_now();
+       fcntl_unlock(lockfd, F_WRLCK, 0, num);
+       for (i = 0; i < num; i++)
+               if (read(from_lockers[0], fill, 1) != 1)
+                       err(1, "Reading from locker children");
+       diff = time_sub(time_now(), start);
+       printf("Children got locks in %lu.%09u\n",
+              (long)diff.tv_sec, (int)diff.tv_nsec);
+
+       /* Children will now exit. */
+       exit(0);
+}