diff options
Diffstat (limited to 'bdb/mutex/tm.c')
-rw-r--r-- | bdb/mutex/tm.c | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/bdb/mutex/tm.c b/bdb/mutex/tm.c new file mode 100644 index 00000000000..4af1b1907a8 --- /dev/null +++ b/bdb/mutex/tm.c @@ -0,0 +1,627 @@ +/* + * Standalone mutex tester for Berkeley DB mutexes. + */ +#include "db_config.h" + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#if defined(HAVE_MUTEX_PTHREADS) || defined(BUILD_PTHREADS_ANYWAY) +#include <pthread.h> +#endif + +#include "db_int.h" + +void exec_proc(); +void tm_file_init(); +void map_file(); +void run_proc(); +void *run_thread(); +void *run_thread_wake(); +void tm_mutex_destroy(); +void tm_mutex_init(); +void tm_mutex_stats(); +void unmap_file(); + +#define MUTEX_WAKEME 0x80 /* Wake-me flag. */ + +DB_ENV dbenv; /* Fake out DB. */ +size_t len; /* Backing file size. */ +int align; /* Mutex alignment in file. */ +int quit; /* End-of-test flag. */ +char *file = "mutex.file"; /* Backing file. */ + +int maxlocks = 20; /* -l: Backing locks. */ +int nlocks = 10000; /* -n: Locks per processes. */ +int nprocs = 20; /* -p: Processes. */ +int child; /* -s: Slave. */ +int nthreads = 1; /* -t: Threads. */ +int verbose; /* -v: Verbosity. */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + extern char *optarg; + pid_t pid; + int ch, eval, i, status; + char *tmpath; + + tmpath = argv[0]; + while ((ch = getopt(argc, argv, "l:n:p:st:v")) != EOF) + switch(ch) { + case 'l': + maxlocks = atoi(optarg); + break; + case 'n': + nlocks = atoi(optarg); + break; + case 'p': + nprocs = atoi(optarg); + break; + case 's': + child = 1; + break; + case 't': + nthreads = atoi(optarg); +#if !defined(HAVE_MUTEX_PTHREADS) && !defined(BUILD_PTHREADS_ANYWAY) + if (nthreads != 1) { + (void)fprintf(stderr, + "tm: pthreads not available or not compiled for this platform.\n"); + return (EXIT_FAILURE); + } +#endif + break; + case 'v': + verbose = 1; + break; + case '?': + default: + (void)fprintf(stderr, + "usage: tm [-v] [-l maxlocks] [-n locks] [-p procs] [-t threads]\n"); + return (EXIT_FAILURE); + } + argc -= optind; + argv += optind; + + /* + * The file layout: + * DB_MUTEX[1] per-thread mutex array lock + * DB_MUTEX[nthreads] per-thread mutex array + * DB_MUTEX[maxlocks] per-lock mutex array + * u_long[maxlocks][2] per-lock ID array + */ + align = ALIGN(sizeof(DB_MUTEX) * 2, MUTEX_ALIGN); + len = + align * (1 + nthreads + maxlocks) + sizeof(u_long) * maxlocks * 2; + printf( + "mutex alignment %d, structure alignment %d, backing file %lu bytes\n", + MUTEX_ALIGN, align, (u_long)len); + + if (child) { + run_proc(); + return (EXIT_SUCCESS); + } + + tm_file_init(); + tm_mutex_init(); + + printf( + "%d proc, %d threads/proc, %d lock requests from %d locks:\n", + nprocs, nthreads, nlocks, maxlocks); + for (i = 0; i < nprocs; ++i) + switch (fork()) { + case -1: + perror("fork"); + return (EXIT_FAILURE); + case 0: + exec_proc(tmpath); + break; + default: + break; + } + + eval = EXIT_SUCCESS; + while ((pid = wait(&status)) != (pid_t)-1) { + fprintf(stderr, + "%lu: exited %d\n", (u_long)pid, WEXITSTATUS(status)); + if (WEXITSTATUS(status) != 0) + eval = EXIT_FAILURE; + } + + tm_mutex_stats(); + tm_mutex_destroy(); + + printf("tm: exit status: %s\n", + eval == EXIT_SUCCESS ? "success" : "failed!"); + return (eval); +} + +void +exec_proc(tmpath) + char *tmpath; +{ + char *argv[10], **ap, b_l[10], b_n[10], b_t[10]; + + ap = &argv[0]; + *ap++ = "tm"; + sprintf(b_l, "-l%d", maxlocks); + *ap++ = b_l; + sprintf(b_n, "-n%d", nlocks); + *ap++ = b_n; + *ap++ = "-s"; + sprintf(b_t, "-t%d", nthreads); + *ap++ = b_t; + if (verbose) + *ap++ = "-v"; + + *ap = NULL; + execvp(tmpath, argv); + + fprintf(stderr, "%s: %s\n", tmpath, strerror(errno)); + exit(EXIT_FAILURE); +} + +void +run_proc() +{ +#if defined(HAVE_MUTEX_PTHREADS) || defined(BUILD_PTHREADS_ANYWAY) + pthread_t *kidsp, wakep; + int i, status; + void *retp; +#endif + __os_sleep(&dbenv, 3, 0); /* Let everyone catch up. */ + + srand((u_int)time(NULL) / getpid()); /* Initialize random numbers. */ + + if (nthreads == 1) /* Simple case. */ + exit((int)run_thread((void *)0)); + +#if defined(HAVE_MUTEX_PTHREADS) || defined(BUILD_PTHREADS_ANYWAY) + /* + * Spawn off threads. We have nthreads all locking and going to + * sleep, and one other thread cycling through and waking them up. + */ + if ((kidsp = + (pthread_t *)calloc(sizeof(pthread_t), nthreads)) == NULL) { + fprintf(stderr, "tm: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + for (i = 0; i < nthreads; i++) + if ((errno = pthread_create( + &kidsp[i], NULL, run_thread, (void *)i)) != 0) { + fprintf(stderr, "tm: failed spawning thread %d: %s\n", + i, strerror(errno)); + exit(EXIT_FAILURE); + } + + if ((errno = pthread_create( + &wakep, NULL, run_thread_wake, (void *)0)) != 0) { + fprintf(stderr, "tm: failed spawning wakeup thread: %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + /* Wait for the threads to exit. */ + status = 0; + for (i = 0; i < nthreads; i++) { + pthread_join(kidsp[i], &retp); + if (retp != NULL) { + fprintf(stderr, + "tm: thread %d exited with error\n", i); + status = EXIT_FAILURE; + } + } + free(kidsp); + + /* Signal wakeup thread to stop. */ + quit = 1; + pthread_join(wakep, &retp); + if (retp != NULL) { + fprintf(stderr, "tm: wakeup thread exited with error\n"); + status = EXIT_FAILURE; + } + + exit(status); +#endif +} + +void * +run_thread(arg) + void *arg; +{ + DB_MUTEX *gm_addr, *lm_addr, *tm_addr, *mp; + u_long gid1, gid2, *id_addr; + int fd, i, lock, id, nl, remap; + + /* Set local and global per-thread ID. */ + id = (int)arg; + gid1 = (u_long)getpid(); +#if defined(HAVE_MUTEX_PTHREADS) || defined(BUILD_PTHREADS_ANYWAY) + gid2 = (u_long)pthread_self(); +#else + gid2 = 0; +#endif + printf("\tPID: %lu; TID: %lx; ID: %d\n", gid1, gid2, id); + + nl = nlocks; + for (gm_addr = NULL, remap = 0;;) { + /* Map in the file as necessary. */ + if (gm_addr == NULL) { + map_file(&gm_addr, &tm_addr, &lm_addr, &id_addr, &fd); + remap = (rand() % 100) + 35; + } + + /* Select and acquire a data lock. */ + lock = rand() % maxlocks; + mp = (DB_MUTEX *)((u_int8_t *)lm_addr + lock * align); + if (verbose) + printf("%lu/%lx: %03d\n", gid1, gid2, lock); + + if (__db_mutex_lock(&dbenv, mp)) { + fprintf(stderr, + "%lu/%lx: never got lock\n", gid1, gid2); + return ((void *)EXIT_FAILURE); + } + if (id_addr[lock * 2] != 0) { + fprintf(stderr, + "RACE! (%lu/%lx granted lock %d held by %lu/%lx)\n", + gid1, gid2, + lock, id_addr[lock * 2], id_addr[lock * 2 + 1]); + return ((void *)EXIT_FAILURE); + } + id_addr[lock * 2] = gid1; + id_addr[lock * 2 + 1] = gid2; + + /* + * Pretend to do some work, periodically checking to see if + * we still hold the mutex. + */ + for (i = 0; i < 3; ++i) { + __os_sleep(&dbenv, 0, rand() % 3); + if (id_addr[lock * 2] != gid1 || + id_addr[lock * 2 + 1] != gid2) { + fprintf(stderr, + "RACE! (%lu/%lx stole lock %d from %lu/%lx)\n", + id_addr[lock * 2], + id_addr[lock * 2 + 1], lock, gid1, gid2); + return ((void *)EXIT_FAILURE); + } + } + +#if defined(HAVE_MUTEX_PTHREADS) || defined(BUILD_PTHREADS_ANYWAY) + /* + * Test self-blocking and unlocking by other threads/processes: + * + * acquire the global lock + * set our wakeup flag + * release the global lock + * acquire our per-thread lock + * + * The wakeup thread will wake us up. + */ + if (__db_mutex_lock(&dbenv, gm_addr)) { + fprintf(stderr, "%lu/%lx: global lock\n", gid1, gid2); + return ((void *)EXIT_FAILURE); + } + mp = (DB_MUTEX *)((u_int8_t *)tm_addr + id * align); + F_SET(mp, MUTEX_WAKEME); + if (__db_mutex_unlock(&dbenv, gm_addr)) { + fprintf(stderr, + "%lu/%lx: per-thread wakeup failed\n", gid1, gid2); + return ((void *)EXIT_FAILURE); + } + if (__db_mutex_lock(&dbenv, mp)) { + fprintf(stderr, + "%lu/%lx: per-thread lock\n", gid1, gid2); + return ((void *)EXIT_FAILURE); + } + /* Time passes... */ + if (F_ISSET(mp, MUTEX_WAKEME)) { + fprintf(stderr, "%lu/%lx: %03d wakeup flag still set\n", + gid1, gid2, id); + return ((void *)EXIT_FAILURE); + } +#endif + + /* Release the data lock. */ + id_addr[lock * 2] = id_addr[lock * 2 + 1] = 0; + mp = (DB_MUTEX *)((u_int8_t *)lm_addr + lock * align); + if (__db_mutex_unlock(&dbenv, mp)) { + fprintf(stderr, "%lu/%lx: wakeup failed\n", gid1, gid2); + return ((void *)EXIT_FAILURE); + } + + if (--nl % 100 == 0) + fprintf(stderr, "%lu/%lx: %d\n", gid1, gid2, nl); + + if (nl == 0 || --remap == 0) { + unmap_file((void *)gm_addr, fd); + gm_addr = NULL; + + if (nl == 0) + break; + + __os_sleep(&dbenv, rand() % 3, 0); + } + } + + return (NULL); +} + +#if defined(HAVE_MUTEX_PTHREADS) || defined(BUILD_PTHREADS_ANYWAY) +/* + * run_thread_wake -- + * Thread to wake up other threads that are sleeping. + */ +void * +run_thread_wake(arg) + void *arg; +{ + DB_MUTEX *gm_addr, *tm_addr, *mp; + int fd, id; + + arg = NULL; + map_file(&gm_addr, &tm_addr, NULL, NULL, &fd); + + /* Loop, waking up sleepers and periodically sleeping ourselves. */ + while (!quit) { + id = 0; + + /* Acquire the global lock. */ +retry: if (__db_mutex_lock(&dbenv, gm_addr)) { + fprintf(stderr, "wt: global lock failed\n"); + return ((void *)EXIT_FAILURE); + } + +next: mp = (DB_MUTEX *)((u_int8_t *)tm_addr + id * align); + if (F_ISSET(mp, MUTEX_WAKEME)) { + F_CLR(mp, MUTEX_WAKEME); + if (__db_mutex_unlock(&dbenv, mp)) { + fprintf(stderr, "wt: wakeup failed\n"); + return ((void *)EXIT_FAILURE); + } + } + + if (++id < nthreads && id % 3 != 0) + goto next; + + if (__db_mutex_unlock(&dbenv, gm_addr)) { + fprintf(stderr, "wt: global unlock failed\n"); + return ((void *)EXIT_FAILURE); + } + + __os_sleep(&dbenv, 0, 500); + + if (id < nthreads) + goto retry; + } + return (NULL); +} +#endif + +/* + * tm_file_init -- + * Initialize the backing file. + */ +void +tm_file_init() +{ + int fd; + + + /* Initialize the backing file. */ + printf("Create the backing file...\n"); +#ifdef HAVE_QNX + (void)shm_unlink(file); + if ((fd = shm_open(file, O_CREAT | O_RDWR | O_TRUNC, +#else + (void)remove(file); + if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC, +#endif + + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) == -1) { + (void)fprintf(stderr, "%s: open: %s\n", file, strerror(errno)); + exit(EXIT_FAILURE); + } + if (lseek(fd, (off_t)len, SEEK_SET) != len || write(fd, &fd, 1) != 1) { + (void)fprintf(stderr, + "%s: seek/write: %s\n", file, strerror(errno)); + exit(EXIT_FAILURE); + } + (void)close(fd); +} + +/* + * tm_mutex_init -- + * Initialize the mutexes. + */ +void +tm_mutex_init() +{ + DB_MUTEX *gm_addr, *lm_addr, *mp, *tm_addr; + int fd, i; + + map_file(&gm_addr, &tm_addr, &lm_addr, NULL, &fd); + + printf("Initialize the global mutex...\n"); + if (__db_mutex_init_int(&dbenv, gm_addr, 0, 0)) { + fprintf(stderr, + "__db_mutex_init (global): %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + printf("Initialize the per-thread mutexes...\n"); + for (i = 1, mp = tm_addr; + i <= nthreads; ++i, mp = (DB_MUTEX *)((u_int8_t *)mp + align)) { + if (__db_mutex_init_int(&dbenv, mp, 0, MUTEX_SELF_BLOCK)) { + fprintf(stderr, "__db_mutex_init (per-thread %d): %s\n", + i, strerror(errno)); + exit(EXIT_FAILURE); + } + if (__db_mutex_lock(&dbenv, mp)) { + fprintf(stderr, + "__db_mutex_init (per-thread %d) lock: %s\n", + i, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + printf("Initialize the per-lock mutexes...\n"); + for (i = 1, mp = lm_addr; + i <= maxlocks; ++i, mp = (DB_MUTEX *)((u_int8_t *)mp + align)) + if (__db_mutex_init_int(&dbenv, mp, 0, 0)) { + fprintf(stderr, "__db_mutex_init (per-lock: %d): %s\n", + i, strerror(errno)); + exit(EXIT_FAILURE); + } + + unmap_file((void *)gm_addr, fd); +} + +/* + * tm_mutex_destroy -- + * Destroy the mutexes. + */ +void +tm_mutex_destroy() +{ + DB_MUTEX *gm_addr, *lm_addr, *mp, *tm_addr; + int fd, i; + + map_file(&gm_addr, &tm_addr, &lm_addr, NULL, &fd); + + printf("Destroy the global mutex...\n"); + if (__db_mutex_destroy(gm_addr)) { + fprintf(stderr, + "__db_mutex_destroy (global): %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + printf("Destroy the per-thread mutexes...\n"); + for (i = 1, mp = tm_addr; + i <= nthreads; ++i, mp = (DB_MUTEX *)((u_int8_t *)mp + align)) { + if (__db_mutex_destroy(mp)) { + fprintf(stderr, + "__db_mutex_destroy (per-thread %d): %s\n", + i, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + printf("Destroy the per-lock mutexes...\n"); + for (i = 1, mp = lm_addr; + i <= maxlocks; ++i, mp = (DB_MUTEX *)((u_int8_t *)mp + align)) + if (__db_mutex_destroy(mp)) { + fprintf(stderr, + "__db_mutex_destroy (per-lock: %d): %s\n", + i, strerror(errno)); + exit(EXIT_FAILURE); + } + + unmap_file((void *)gm_addr, fd); +#ifdef HAVE_QNX + (void)shm_unlink(file); +#endif +} + +/* + * tm_mutex_stats -- + * Display mutex statistics. + */ +void +tm_mutex_stats() +{ + DB_MUTEX *gm_addr, *lm_addr, *mp; + int fd, i; + + map_file(&gm_addr, NULL, &lm_addr, NULL, &fd); + + printf("Per-lock mutex statistics...\n"); + for (i = 1, mp = lm_addr; + i <= maxlocks; ++i, mp = (DB_MUTEX *)((u_int8_t *)mp + align)) + printf("mutex %2d: wait: %lu; no wait %lu\n", i, + (u_long)mp->mutex_set_wait, (u_long)mp->mutex_set_nowait); + + unmap_file((void *)gm_addr, fd); +} + +/* + * map_file -- + * Map in the backing file. + */ +void +map_file(gm_addrp, tm_addrp, lm_addrp, id_addrp, fdp) + DB_MUTEX **gm_addrp, **tm_addrp, **lm_addrp; + u_long **id_addrp; + int *fdp; +{ + void *maddr; + int fd; + +#ifndef MAP_FAILED +#define MAP_FAILED (void *)-1 +#endif +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif +#ifdef HAVE_QNX + if ((fd = shm_open(file, O_RDWR, 0)) == -1) { +#else + if ((fd = open(file, O_RDWR, 0)) == -1) { +#endif + fprintf(stderr, "%s: open %s\n", file, strerror(errno)); + exit(EXIT_FAILURE); + } + + maddr = mmap(NULL, len, + PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, (off_t)0); + if (maddr == MAP_FAILED) { + fprintf(stderr, "%s: mmap: %s\n", file, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (gm_addrp != NULL) + *gm_addrp = (DB_MUTEX *)maddr; + maddr = (u_int8_t *)maddr + align; + if (tm_addrp != NULL) + *tm_addrp = (DB_MUTEX *)maddr; + maddr = (u_int8_t *)maddr + align * nthreads; + if (lm_addrp != NULL) + *lm_addrp = (DB_MUTEX *)maddr; + maddr = (u_int8_t *)maddr + align * maxlocks; + if (id_addrp != NULL) + *id_addrp = (u_long *)maddr; + if (fdp != NULL) + *fdp = fd; +} + +/* + * unmap_file -- + * Discard backing file map. + */ +void +unmap_file(maddr, fd) + void *maddr; + int fd; +{ + if (munmap(maddr, len) != 0) { + fprintf(stderr, "munmap: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + if (close(fd) != 0) { + fprintf(stderr, "close: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } +} |