/* test a lock wait idea Copyright (C) Andrew Tridgell 2006 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 . */ #include "includes.h" #include "lib/events/events.h" #include "system/filesys.h" #include "system/wait.h" #include "popt.h" #include "cmdline.h" struct lockwait_handle { struct fd_event *fde; int fd[2]; pid_t child; void *private_data; void (*callback)(void *); }; static void lockwait_handler(struct event_context *ev, struct fd_event *fde, uint16_t flags, void *private_data) { struct lockwait_handle *h = talloc_get_type(private_data, struct lockwait_handle); void (*callback)(void *) = h->callback; void *p = h->private_data; talloc_set_destructor(h, NULL); close(h->fd[0]); talloc_free(h); callback(p); waitpid(h->child, NULL, 0); } static int lockwait_destructor(struct lockwait_handle *h) { close(h->fd[0]); kill(h->child, SIGKILL); waitpid(h->child, NULL, 0); return 0; } static struct lockwait_handle *lockwait(struct event_context *ev, TALLOC_CTX *mem_ctx, int fd, off_t ofs, size_t len, void (*callback)(void *), void *private_data) { struct lockwait_handle *h; int ret; h = talloc_zero(mem_ctx, struct lockwait_handle); if (h == NULL) { return NULL; } ret = pipe(h->fd); if (ret != 0) { talloc_free(h); return NULL; } h->child = fork(); if (h->child == (pid_t)-1) { close(h->fd[0]); close(h->fd[1]); talloc_free(h); return NULL; } h->callback = callback; h->private_data = private_data; if (h->child == 0) { /* in child */ struct flock lock; close(h->fd[0]); lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = ofs; lock.l_len = len; lock.l_pid = 0; fcntl(fd,F_SETLKW,&lock); _exit(0); } close(h->fd[1]); talloc_set_destructor(h, lockwait_destructor); h->fde = event_add_fd(ev, h, h->fd[0], EVENT_FD_READ, lockwait_handler, h); if (h->fde == NULL) { talloc_free(h); return NULL; } return h; } static void fcntl_lock_callback(void *p) { int *got_lock = (int *)p; *got_lock = 1; } /* get an fcntl lock - waiting if necessary */ static int fcntl_lock(struct event_context *ev, int fd, int op, off_t offset, off_t count, int type) { struct flock lock; int ret; int use_lockwait = (op == F_SETLKW); int got_lock = 0; lock.l_type = type; lock.l_whence = SEEK_SET; lock.l_start = offset; lock.l_len = count; lock.l_pid = 0; do { ret = fcntl(fd,use_lockwait?F_SETLK:op,&lock); if (ret == 0) { return 0; } if (ret == -1 && (errno == EACCES || errno == EAGAIN || errno == EDEADLK)) { struct lockwait_handle *h; h = lockwait(ev, ev, fd, offset, count, fcntl_lock_callback, &got_lock); if (h == NULL) { errno = ENOLCK; return -1; } /* in real code we would return to the event loop */ while (!got_lock) { event_loop_once(ev); } got_lock = 0; } } while (!got_lock); return ret; } static void child(struct event_context *ev, int n) { int fd; int count=0; struct timeval tv; fd = open("test.dat", O_CREAT|O_RDWR, 0666); if (fd == -1) { perror("test.dat"); exit(1); } tv = timeval_current(); while (timeval_elapsed(&tv) < 10) { int ret; ret = fcntl_lock(ev, fd, F_SETLKW, 0, 1, F_WRLCK); if (ret != 0) { printf("Failed to get lock in child %d!\n", n); break; } fcntl_lock(ev, fd, F_SETLK, 0, 1, F_UNLCK); count++; } printf("child %2d %.0f ops/sec\n", n, count/timeval_elapsed(&tv)); _exit(0); } static int timelimit = 10; /* main program */ int main(int argc, const char *argv[]) { pid_t *pids; int nprogs = 2; int i; struct event_context *ev; struct poptOption popt_options[] = { POPT_AUTOHELP { "timelimit", 't', POPT_ARG_INT, &timelimit, 0, "timelimit", "integer" }, { "num-progs", 'n', POPT_ARG_INT, &nprogs, 0, "num_progs", "integer" }, POPT_TABLEEND }; poptContext pc; int opt; pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST); while ((opt = poptGetNextOpt(pc)) != -1) { switch (opt) { default: fprintf(stderr, "Invalid option %s: %s\n", poptBadOption(pc, 0), poptStrerror(opt)); exit(1); } } ev = event_context_init(NULL); pids = talloc_array(ev, pid_t, nprogs); /* create N processes fighting over the same lock */ for (i=0;i