#include <sys/types.h> #ifndef __NetBSD__ extern char *__progname; # define getprogname() __progname # ifdef __GNUC__ # define atomic_cas_32(ptr, expected, new) \ __sync_val_compare_and_swap(ptr, expected, new) # define atomic_add_32(ptr, incr) \ __sync_fetch_and_add(ptr, incr) # else # include <sys/atomic.h> # endif #endif #include <sys/mman.h> #include <stdio.h> #include <fcntl.h> #include <inttypes.h> #include <stdint.h> #include <stdlib.h> #include <unistd.h> #include <err.h> struct state { uint32_t pid; uint32_t count; uint32_t owner; uint32_t todo; }; static int debug = 0; static uint32_t batch = 100; /* How many files per turn */ static uint32_t workers = 10; /* How many processes */ static uint32_t repetitions = 10; /* How many iterations */ static void makefiles(struct state *s) { uint32_t i, e; uint32_t pid, spid, rpid; int fd; char buf[1024]; pid = spid = s->pid; if (debug) fprintf(stderr, "%s: enter pid %d %d %d\n", __func__, spid, s->owner, s->count); if ((rpid = atomic_cas_32(&s->owner, 0, spid)) != 0) goto out; if (debug) fprintf(stderr, "%s: work pid %d %d\n", __func__, spid, s->count); for (i = s->count, e = i + batch; i < e; i++) { snprintf(buf, sizeof(buf), "%d.%d", pid, i); if ((fd = open(buf, O_CREAT|O_TRUNC, 0600)) == -1) err(EXIT_FAILURE, "%s: can't create pid %d `%s'", __func__, spid, buf); close(fd); } s->count += batch; s->todo = 1; if (atomic_cas_32(&s->owner, spid, 0) != spid) errx(EXIT_FAILURE, "%s: can't unlock pid %d `%s'", __func__, spid, buf); out: if (debug) fprintf(stderr, "%s: exit pid %d %d %d\n", __func__, spid, rpid, s->count); } static int removefiles(struct state *s, uint32_t spid) { uint32_t i, e, rpid; uint32_t pid = s->pid; char buf[1024]; if (debug) fprintf(stderr, "%s: enter pid %d[%d] %d %d\n", __func__, spid, s->pid, s->owner, s->count); if ((rpid = atomic_cas_32(&s->owner, 0, spid)) != 0) goto out; if (debug) fprintf(stderr, "%s: work pid %d %d\n", __func__, spid, s->count); if (s->todo) { for (e = s->count, i = e - batch; i < e; i++) { snprintf(buf, sizeof(buf), "%d.%d", pid, i); if (unlink(buf) == -1) err(EXIT_FAILURE, "%s: can't unlink pid %d `%s'", __func__, spid, buf); } s->todo = 0; rpid = spid; } if (atomic_cas_32(&s->owner, spid, 0) != spid) errx(EXIT_FAILURE, "%s: can't unlock pid %d `%s'", __func__, spid, buf); out: if (debug) fprintf(stderr, "%s: exit pid %d[%d] %d %d\n", __func__, spid, s->pid, rpid, s->count); return rpid != 0; } static void makeworker(struct state *s, size_t i) { pid_t pid; size_t j, r; switch (pid = fork()) { case 0: s[i].pid = getpid(); for (r = 0; r < repetitions; r++) { int got; makefiles(&s[i]); do { got = 0; for (j = 0; j < workers; j++) got |= removefiles(&s[j], s[i].pid); } while (got); } exit(EXIT_SUCCESS); case -1: err(EXIT_FAILURE, "fork"); default: return; } } static void usage(void) { fprintf(stderr, "Usage: %s [-d] [-b <batch>] [-r <repetitions>] " "[-w <workers>]\n", getprogname()); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { uint32_t i; int s; void *p; while ((s = getopt(argc, argv, "b:dr:w:")) != -1) switch (s) { case 'd': debug++; break; case 'b': batch = atoi(optarg); break; case 'r': repetitions = atoi(optarg); break; case 'w': workers = atoi(optarg); break; default: usage(); } p = mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); if (p == MAP_FAILED) err(EXIT_FAILURE, "mmap"); for (i = 0; i < workers; i++) makeworker(p, i); for (i = 0; i < workers; i++) if (wait(&s) == -1) err(EXIT_FAILURE, "wait"); return 0; }