📄 tm.c
字号:
/* * 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. */intmain(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);}voidexec_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);}voidrun_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. */voidtm_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. */voidtm_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. */voidtm_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. */voidtm_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. */voidmap_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. */voidunmap_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); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -