fcntl spinlock in Linux?
Rusty Russell
rusty at samba.org
Wed Aug 14 20:23:05 MDT 2013
Jeff Layton <jlayton at samba.org> writes:
> Rusty,
>
> I'm working some more on this problem. Would you have any issue with me
> modifying the testcase you have below and adding it to the testsuite I
> have for testing these patches?
Sure! Here's the copyright notice to prepend:
/* 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/>. */
Cheers,
Rusty.
>
>> // 2>/dev/null; set -e; OUT=/tmp/`basename $0 .c`; if [ ! -f "$OUT" ] || [ "$OUT" -ot "$0" ]; then gcc -Wall -g -o "$OUT".$$ $0 -lrt && mv "$OUT".$$ "$OUT"; fi; exec "$OUT" "$@"
>> #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);
>> }
>
>
> --
> Jeff Layton <jlayton at samba.org>
More information about the samba-technical
mailing list