diff mbox

[v0,2/2] posix04: add new "posix04" test

Message ID 1425289405-13365-3-git-send-email-daniel.wagner@bmw-carit.de (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel Wagner March 2, 2015, 9:43 a.m. UTC
The dinning philosopher locking 'problem'. A given number of clients
try to lock on a file its own region and than the region 'right' from
its own region.

Signed-off-by: Daniel Wagner <daniel.wagner@bmw-carit.de>
---
 Makefile.am |   2 +-
 posix04.c   | 288 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 289 insertions(+), 1 deletion(-)
 create mode 100644 posix04.c
diff mbox

Patch

diff --git a/Makefile.am b/Makefile.am
index 4a2ae98..c2eb816 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,6 +4,6 @@  ACLOCAL_AMFLAGS = -I m4
 EXTRA_DIST = m4/gnulib-cache.m4
 LDADD = lib/libgnu.a -lrt
 
-bin_PROGRAMS = flock01 flock02 posix01 posix02 posix03 lease01 lease02
+bin_PROGRAMS = flock01 flock02 posix01 posix02 posix03 posix04 lease01 lease02
 
 SUBDIRS = lib/
diff --git a/posix04.c b/posix04.c
new file mode 100644
index 0000000..ed862f2
--- /dev/null
+++ b/posix04.c
@@ -0,0 +1,288 @@ 
+/*
+ * POSIX deadlock detection performance test
+ *
+ * The dinning philosopher locking 'problem'. A given number of
+ * clients try to lock on a file its own region and than the region
+ * 'right' from its own region.
+ *
+ * Obviously, We could just give them enough forks or a gun. Both
+ * solves the problem once for all but that's no fun.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#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>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "timespec.h"
+
+#define DEFAULT_PROCESSES 10
+#define DEFAULT_ITERATIONS 10000
+
+#define TFR TEMP_FAILURE_RETRY
+
+struct child_context
+{
+	pid_t pid;
+	struct timespec diff;
+	unsigned int deadlock;
+};
+
+static struct child_context *ctxs;
+
+/* Taken from TDB: LGPLv3 */
+static int fcntl_lock(int fd, int rw, off_t off, off_t len, bool waitflag)
+{
+	struct flock fl;
+
+	/*
+	fprintf(stderr, "%d: lock\tfd %d off %5zd len %5zd\n",
+		getpid(), fd, off, len);
+	*/
+
+	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, 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;
+
+	/*
+	fprintf(stderr, "%d: unlock\tfd %d off %5zd len %5zd\n",
+		getpid(), fd, off, len);
+	*/
+
+	return fcntl(fd, F_SETLKW, &fl);
+}
+
+static int
+lockunlock(int fd, int id, int nproc)
+{
+	int ret, nid;
+	off_t off, len;
+	off_t noff, nlen;
+
+	nid = (id + 1) % nproc;
+
+	off = id * 10;
+	len = id * 10 + 10;
+
+	noff = nid * 10;
+	nlen = nid * 10 + 10;
+
+	ret = fcntl_lock(fd, F_WRLCK, off, len, true);
+	if (ret)
+		return ret;
+
+	ret = fcntl_lock(fd, F_WRLCK, noff, nlen, true);
+	if (ret)
+		goto err;
+
+	if (fcntl_unlock(fd, noff, nlen))
+		perror("unlock");
+
+err:
+	if (fcntl_unlock(fd, off, len))
+		perror("unlock");
+
+	return ret;
+}
+
+static int do_child(int fd, int id, int nproc, int to_lockers, int from_lockers)
+{
+	struct timespec start, end;
+	unsigned char c;
+	int ret;
+
+	while(TFR(read(to_lockers, &c, 1)) == 1) {
+		if (c != 'g')
+			return 0;
+
+		ret = clock_gettime(CLOCK_MONOTONIC_RAW, &start);
+		if (ret) {
+			perror("clock_gettime");
+			return ret;
+		}
+
+		if (lockunlock(fd, id, nproc) != 0)
+			ctxs[id].deadlock++;
+
+		ret = clock_gettime(CLOCK_MONOTONIC_RAW, &end);
+		if (ret) {
+			perror("clock_gettime");
+			return ret;
+		}
+
+		ctxs[id].diff = timespec_add(ctxs[id].diff,
+					timespec_sub(end, start));
+
+		if (TFR(write(from_lockers, &c, 1)) != 1)
+			fprintf(stderr, "Writing to parent");
+	}
+
+	return 0;
+}
+
+static int
+usage(char *argv0)
+{
+	errx(1, "Usage: %s [-i iterations] [-n nr_children] [-s] <filename>", argv0);
+}
+
+int main(int argc, char *argv[])
+{
+	int nproc = DEFAULT_PROCESSES, i, opt, valid = 0, stats = 0;
+	int iter = DEFAULT_ITERATIONS;
+	const char *filename;
+	int to_lockers[2], from_lockers[2], fd;
+	struct timespec total = { .tv_sec = 0,
+				  .tv_nsec = 0 };
+	unsigned char wc, rc;
+	unsigned int deadlock = 0;
+
+	total.tv_sec = 0;
+	total.tv_nsec = 0;
+
+	while ((opt = getopt(argc, argv, "i:n:s")) != -1) {
+		switch (opt) {
+		case 'i':
+			iter = atoi(optarg);
+			break;
+		case 'n':
+			nproc = atoi(optarg);
+			break;
+		case 's':
+			stats = 1;
+			break;
+		default:
+			usage(argv[0]);
+		}
+	}
+
+	if (nproc < 2) {
+		fprintf(stderr, "Invalid argument: at least 2 child process needed\n");
+		return 1;
+	}
+
+	filename = argv[optind];
+	if (!filename)
+		usage(argv[0]);
+
+	ctxs = mmap(0, nproc * sizeof(*ctxs), PROT_READ | PROT_WRITE,
+			MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+
+	if (ctxs == (struct child_context *)-1) {
+		fprintf(stderr, "Unable to allocate child context array!");
+		return 1;
+	}
+
+	if (pipe(to_lockers))
+		err(1, "pipe (to_lockers)");
+	if (pipe(from_lockers))
+		err(1, "pipe (from_lockers)");
+
+	fd = open(filename, O_CREAT|O_RDWR, 0644);
+	if (fd < 0) {
+		perror("open");
+		return 1;
+	}
+
+	for (i = 0; i < nproc; i++) {
+		ctxs[i].pid = fork();
+		if (!ctxs[i].pid)
+			return do_child(fd, i, nproc,
+					to_lockers[0], from_lockers[1]);
+	}
+
+	close(to_lockers[0]);
+	close(from_lockers[1]);
+
+	wc = 'g'; /* go */
+	while(iter--) {
+		for (i = 0; i < nproc; i++) {
+			if (TFR(write(to_lockers[1], &wc, 1)) != 1) {
+				perror("write");
+			}
+		}
+
+		for (i = 0; i < nproc; i++) {
+			if (TFR(read(from_lockers[0], &rc, 1)) != 1)
+				break;
+		}
+	}
+
+	wc = '\0'; /* stop */
+	for (i = 0; i < nproc; i++) {
+		if (TFR(write(to_lockers[1], &wc, 1)) != 1) {
+			perror("write");
+		}
+	}
+
+	for (i = 0; i < nproc; ++i) {
+		int status;
+
+		if (ctxs[i].pid < 0) {
+			fprintf(stderr, "process %d failed to fork\n", i);
+			continue;
+		}
+		if (waitpid(ctxs[i].pid, &status, 0) < 0) {
+			fprintf(stderr, "unable to reap pid %d\n", ctxs[i].pid);
+			continue;
+		}
+		if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+			fprintf(stderr, "pid %d exited abnormally(0x%x)\n",
+				ctxs[i].pid, status);
+			continue;
+		}
+		total = timespec_add(total, ctxs[i].diff);
+		deadlock += ctxs[i].deadlock;
+		++valid;
+
+	}
+
+	close(fd);
+
+	if (valid != nproc) {
+		fprintf(stderr, "Some children didn't run properly -- "
+				"requested %d but only got %d\n", nproc, valid);
+		return 1;
+	}
+
+	if (stats)
+		printf("deadlocks %u\n", deadlock);
+	printf("%ld.%09ld\n", total.tv_sec, total.tv_nsec);
+
+	return 0;
+}