Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

tm.c

00001 /*
00002  * Standalone mutex tester for Berkeley DB mutexes.
00003  *
00004  * $Id: tm.c,v 12.10 2005/10/21 17:53:04 bostic Exp $
00005  */
00006 #include "db_config.h"
00007 
00008 #ifndef NO_SYSTEM_INCLUDES
00009 #include <sys/types.h>
00010 #include <sys/wait.h>
00011 
00012 #include <errno.h>
00013 #include <signal.h>
00014 #include <stdio.h>
00015 #include <stdlib.h>
00016 #include <string.h>
00017 #include <time.h>
00018 
00019 #if defined(MUTEX_THREAD_TEST)
00020 #include <pthread.h>
00021 #endif
00022 #endif
00023 
00024 #include "db_int.h"
00025 
00026 #ifdef DB_WIN32
00027 extern int getopt(int, char * const *, const char *);
00028 
00029 typedef HANDLE os_pid_t;
00030 typedef HANDLE os_thread_t;
00031 
00032 #define os_thread_create(thrp, attr, func, arg)                         \
00033     (((*(thrp) = CreateThread(NULL, 0,                                  \
00034         (LPTHREAD_START_ROUTINE)(func), (arg), 0, NULL)) == NULL) ? -1 : 0)
00035 #define os_thread_join(thr, statusp)                                    \
00036     ((WaitForSingleObject((thr), INFINITE) == WAIT_OBJECT_0) &&         \
00037     GetExitCodeThread((thr), (LPDWORD)(statusp)) ? 0 : -1)
00038 #define os_thread_self() GetCurrentThreadId()
00039 
00040 #else /* !DB_WIN32 */
00041 
00042 typedef pid_t os_pid_t;
00043 
00044 #ifdef MUTEX_THREAD_TEST
00045 typedef pthread_t os_thread_t;
00046 #endif
00047 
00048 #define os_thread_create(thrp, attr, func, arg)                         \
00049     pthread_create((thrp), (attr), (func), (arg))
00050 #define os_thread_join(thr, statusp) pthread_join((thr), (statusp))
00051 #define os_thread_self() pthread_self()
00052 #endif
00053 
00054 #define OS_BAD_PID (os_pid_t)-1
00055 
00056 #define TESTDIR         "TESTDIR"               /* Working area */
00057 #define MT_FILE         "TESTDIR/mutex.file"
00058 #define MT_FILE_QUIT    "TESTDIR/mutex.file.quit"
00059 
00060 /*
00061  * The backing file layout:
00062  *      TM[1]                   per-thread mutex array lock
00063  *      TM[nthreads]            per-thread mutex array
00064  *      TM[maxlocks]            per-lock mutex array
00065  */
00066 typedef struct {
00067         db_mutex_t mutex;                       /* Mutex. */
00068         u_long     id;                          /* Holder's ID. */
00069         u_int      wakeme;                      /* Request to awake. */
00070 } TM;
00071 
00072 DB_ENV  *dbenv;                                 /* Backing environment */
00073 size_t   len;                                   /* Backing file size. */
00074 
00075 u_int8_t *gm_addr;                              /* Global mutex */
00076 u_int8_t *lm_addr;                              /* Locker mutexes */
00077 u_int8_t *tm_addr;                              /* Thread mutexes */
00078 
00079 #ifdef MUTEX_THREAD_TEST
00080 os_thread_t *kidsp;                             /* Locker threads */
00081 os_thread_t  wakep;                             /* Wakeup thread */
00082 #endif
00083 
00084 int      maxlocks = 20;                         /* -l: Backing locks. */
00085 int      nlocks = 10000;                        /* -n: Locks per processes. */
00086 int      nprocs = 20;                           /* -p: Processes. */
00087 int      nthreads = 1;                          /* -t: Threads. */
00088 int      verbose;                               /* -v: Verbosity. */
00089 
00090 int      locker_start(u_long);
00091 int      locker_wait(void);
00092 void     map_file(u_int8_t **, u_int8_t **, u_int8_t **, DB_FH **);
00093 os_pid_t os_spawn(const char *, char *const[]);
00094 int      os_wait(os_pid_t *, int);
00095 void    *run_lthread(void *);
00096 void    *run_wthread(void *);
00097 os_pid_t spawn_proc(u_long, char *, char *);
00098 void     tm_env_close(void);
00099 int      tm_env_init(void);
00100 void     tm_file_init(void);
00101 void     tm_mutex_destroy(void);
00102 void     tm_mutex_init(void);
00103 void     tm_mutex_stats(void);
00104 void     unmap_file(u_int8_t *, DB_FH *);
00105 int      usage(void);
00106 int      wakeup_start(u_long);
00107 int      wakeup_wait(void);
00108 
00109 int
00110 main(argc, argv)
00111         int argc;
00112         char *argv[];
00113 {
00114         enum {LOCKER, WAKEUP, PARENT} rtype;
00115         extern int optind;
00116         extern char *optarg;
00117         os_pid_t wakeup_pid, *pids;
00118         u_long id;
00119         DB_FH *fhp, *map_fhp;
00120         int ch, err, i;
00121         char *p, *tmpath, cmd[1024];
00122 
00123         rtype = PARENT;
00124         id = 0;
00125         tmpath = argv[0];
00126         while ((ch = getopt(argc, argv, "l:n:p:T:t:v")) != EOF)
00127                 switch (ch) {
00128                 case 'l':
00129                         maxlocks = atoi(optarg);
00130                         break;
00131                 case 'n':
00132                         nlocks = atoi(optarg);
00133                         break;
00134                 case 'p':
00135                         nprocs = atoi(optarg);
00136                         break;
00137                 case 't':
00138                         if ((nthreads = atoi(optarg)) == 0)
00139                                 nthreads = 1;
00140 #if !defined(MUTEX_THREAD_TEST)
00141                         if (nthreads != 1) {
00142                                 (void)fprintf(stderr,
00143     "tm: thread support not available or not compiled for this platform.\n");
00144                                 return (EXIT_FAILURE);
00145                         }
00146 #endif
00147                         break;
00148                 case 'T':
00149                         if (!memcmp(optarg, "locker", sizeof("locker") - 1))
00150                                 rtype = LOCKER;
00151                         else if (
00152                             !memcmp(optarg, "wakeup", sizeof("wakeup") - 1))
00153                                 rtype = WAKEUP;
00154                         else
00155                                 return (usage());
00156                         if ((p = strchr(optarg, '=')) == NULL)
00157                                 return (usage());
00158                         id = atoi(p + 1);
00159                         break;
00160                 case 'v':
00161                         verbose = 1;
00162                         break;
00163                 case '?':
00164                 default:
00165                         return (usage());
00166                 }
00167         argc -= optind;
00168         argv += optind;
00169 
00170         /*
00171          * If we're not running a multi-process test, we should be running
00172          * a multi-thread test.
00173          */
00174         if (nprocs == 1 && nthreads == 1) {
00175                 fprintf(stderr,
00176             "tm: running in a single process requires multiple threads\n");
00177                 return (EXIT_FAILURE);
00178         }
00179 
00180         len = sizeof(TM) * (1 + nthreads * nprocs + maxlocks);
00181 
00182         /*
00183          * In the multi-process test, the parent spawns processes that exec
00184          * the original binary, ending up here.  Each process joins the DB
00185          * environment separately and then calls the supporting function.
00186          */
00187         if (rtype == LOCKER || rtype == WAKEUP) {
00188                 __os_sleep(dbenv, 3, 0);        /* Let everyone catch up. */
00189                                                 /* Initialize random numbers. */
00190                 srand((u_int)time(NULL) % getpid());
00191 
00192                 if (tm_env_init() != 0)         /* Join the environment. */
00193                         exit(EXIT_FAILURE);
00194                                                 /* Join the backing file. */
00195                 map_file(&gm_addr, &tm_addr, &lm_addr, &map_fhp);
00196                 if (verbose)
00197                         printf(
00198             "Backing file: global (%#lx), threads (%#lx), locks (%#lx)\n",
00199                             (u_long)gm_addr, (u_long)tm_addr, (u_long)lm_addr);
00200 
00201                 if ((rtype == LOCKER ?
00202                     locker_start(id) : wakeup_start(id)) != 0)
00203                         exit(EXIT_FAILURE);
00204                 if ((rtype == LOCKER ? locker_wait() : wakeup_wait()) != 0)
00205                         exit(EXIT_FAILURE);
00206 
00207                 unmap_file(gm_addr, map_fhp);   /* Detach from backing file. */
00208 
00209                 tm_env_close();                 /* Detach from environment. */
00210 
00211                 exit(EXIT_SUCCESS);
00212         }
00213 
00214         /*
00215          * The following code is only executed by the original parent process.
00216          *
00217          * Clean up from any previous runs.
00218          */
00219         snprintf(cmd, sizeof(cmd), "rm -rf %s", TESTDIR);
00220         (void)system(cmd);
00221         snprintf(cmd, sizeof(cmd), "mkdir %s", TESTDIR);
00222         (void)system(cmd);
00223 
00224         printf(
00225     "tm: %d processes, %d threads/process, %d lock requests from %d locks\n",
00226             nprocs, nthreads, nlocks, maxlocks);
00227         printf("tm: backing file %lu bytes\n", (u_long)len);
00228 
00229         if (tm_env_init() != 0)         /* Create the environment. */
00230                 exit(EXIT_FAILURE);
00231 
00232         tm_file_init();                 /* Initialize backing file. */
00233 
00234                                         /* Map in the backing file. */
00235         map_file(&gm_addr, &tm_addr, &lm_addr, &map_fhp);
00236         if (verbose)
00237                 printf(
00238             "backing file: global (%#lx), threads (%#lx), locks (%#lx)\n",
00239                     (u_long)gm_addr, (u_long)tm_addr, (u_long)lm_addr);
00240 
00241         tm_mutex_init();                /* Initialize mutexes. */
00242 
00243         if (nprocs > 1) {               /* Run the multi-process test. */
00244                 /* Allocate array of locker process IDs. */
00245                 if ((pids = calloc(nprocs, sizeof(os_pid_t))) == NULL) {
00246                         fprintf(stderr, "tm: %s\n", strerror(errno));
00247                         goto fail;
00248                 }
00249 
00250                 /* Spawn locker processes and threads. */
00251                 for (i = 0; i < nprocs; ++i) {
00252                         if ((pids[i] =
00253                             spawn_proc(id, tmpath, "locker")) == OS_BAD_PID) {
00254                                 fprintf(stderr,
00255                                     "tm: failed to spawn a locker\n");
00256                                 goto fail;
00257                         }
00258                         id += nthreads;
00259                 }
00260 
00261                 /* Spawn wakeup process/thread. */
00262                 if ((wakeup_pid =
00263                     spawn_proc(id, tmpath, "wakeup")) == OS_BAD_PID) {
00264                         fprintf(stderr, "tm: failed to spawn waker\n");
00265                         goto fail;
00266                 }
00267                 ++id;
00268 
00269                 /* Wait for all lockers to exit. */
00270                 if ((err = os_wait(pids, nprocs)) != 0) {
00271                         fprintf(stderr, "locker wait failed with %d\n", err);
00272                         goto fail;
00273                 }
00274 
00275                 /* Signal wakeup process to exit. */
00276                 if ((err = __os_open(
00277                     dbenv, MT_FILE_QUIT, DB_OSO_CREATE, 0664, &fhp)) != 0) {
00278                         fprintf(stderr, "tm: open %s\n", db_strerror(err));
00279                         goto fail;
00280                 }
00281                 (void)__os_closehandle(dbenv, fhp);
00282 
00283                 /* Wait for wakeup process/thread. */
00284                 if ((err = os_wait(&wakeup_pid, 1)) != 0) {
00285                         fprintf(stderr,
00286                             "%lu: exited %d\n", (u_long)wakeup_pid, err);
00287                         goto fail;
00288                 }
00289         } else {                        /* Run the single-process test. */
00290                 /* Spawn locker threads. */
00291                 if (locker_start(0) != 0)
00292                         goto fail;
00293 
00294                 /* Spawn wakeup thread. */
00295                 if (wakeup_start(nthreads) != 0)
00296                         goto fail;
00297 
00298                 /* Wait for all lockers to exit. */
00299                 if (locker_wait() != 0)
00300                         goto fail;
00301 
00302                 /* Signal wakeup process to exit. */
00303                 if ((err = __os_open(
00304                     dbenv, MT_FILE_QUIT, DB_OSO_CREATE, 0664, &fhp)) != 0) {
00305                         fprintf(stderr, "tm: open %s\n", db_strerror(err));
00306                         goto fail;
00307                 }
00308                 (void)__os_closehandle(dbenv, fhp);
00309 
00310                 /* Wait for wakeup thread. */
00311                 if (wakeup_wait() != 0)
00312                         goto fail;
00313         }
00314 
00315         tm_mutex_stats();               /* Display run statistics. */
00316         tm_mutex_destroy();             /* Destroy mutexes. */
00317 
00318         unmap_file(gm_addr, map_fhp);   /* Detach from backing file. */
00319 
00320         tm_env_close();                 /* Detach from environment. */
00321 
00322         printf("tm: test succeeded\n");
00323         return (EXIT_SUCCESS);
00324 
00325 fail:   printf("tm: FAILED!\n");
00326         return (EXIT_FAILURE);
00327 }
00328 
00329 int
00330 locker_start(id)
00331         u_long id;
00332 {
00333 #if defined(MUTEX_THREAD_TEST)
00334         int err, i;
00335 
00336         /*
00337          * Spawn off threads.  We have nthreads all locking and going to
00338          * sleep, and one other thread cycling through and waking them up.
00339          */
00340         if ((kidsp =
00341             (os_thread_t *)calloc(sizeof(os_thread_t), nthreads)) == NULL) {
00342                 fprintf(stderr, "tm: %s\n", strerror(errno));
00343                 return (1);
00344         }
00345         for (i = 0; i < nthreads; i++)
00346                 if ((err = os_thread_create(
00347                     &kidsp[i], NULL, run_lthread, (void *)(id + i))) != 0) {
00348                         fprintf(stderr, "tm: failed spawning thread: %s\n",
00349                             db_strerror(err));
00350                         return (1);
00351                 }
00352         return (0);
00353 #else
00354         return (run_lthread((void *)id) == NULL ? 0 : 1);
00355 #endif
00356 }
00357 
00358 int
00359 locker_wait()
00360 {
00361 #if defined(MUTEX_THREAD_TEST)
00362         int i;
00363         void *retp;
00364 
00365         /* Wait for the threads to exit. */
00366         for (i = 0; i < nthreads; i++) {
00367                 os_thread_join(kidsp[i], &retp);
00368                 if (retp != NULL) {
00369                         fprintf(stderr, "tm: thread exited with error\n");
00370                         return (1);
00371                 }
00372         }
00373         free(kidsp);
00374 #endif
00375         return (0);
00376 }
00377 
00378 void *
00379 run_lthread(arg)
00380         void *arg;
00381 {
00382         TM *gp, *mp, *tp;
00383         u_long id, tid;
00384         int err, i, lock, nl;
00385 
00386         id = (uintptr_t)arg;
00387 #if defined(MUTEX_THREAD_TEST)
00388         tid = (u_long)os_thread_self();
00389 #else
00390         tid = 0;
00391 #endif
00392         printf("Locker: ID %03lu (PID: %lu; TID: %lx)\n",
00393             id, (u_long)getpid(), tid);
00394 
00395         gp = (TM *)gm_addr;
00396         tp = (TM *)(tm_addr + id * sizeof(TM));
00397 
00398         for (nl = nlocks; nl > 0;) {
00399                 /* Select and acquire a data lock. */
00400                 lock = rand() % maxlocks;
00401                 mp = (TM *)(lm_addr + lock * sizeof(TM));
00402                 if (verbose)
00403                         printf("%03lu: lock %d (mtx: %lu)\n",
00404                             id, lock, (u_long)mp->mutex);
00405 
00406                 if ((err = dbenv->mutex_lock(dbenv, mp->mutex)) != 0) {
00407                         fprintf(stderr, "%03lu: never got lock %d: %s\n",
00408                             id, lock, db_strerror(err));
00409                         return ((void *)1);
00410                 }
00411                 if (mp->id != 0) {
00412                         fprintf(stderr,
00413                             "RACE! (%03lu granted lock %d held by %03lu)\n",
00414                             id, lock, mp->id);
00415                         return ((void *)1);
00416                 }
00417                 mp->id = id;
00418 
00419                 /*
00420                  * Pretend to do some work, periodically checking to see if
00421                  * we still hold the mutex.
00422                  */
00423                 for (i = 0; i < 3; ++i) {
00424                         __os_sleep(dbenv, 0, rand() % 3);
00425                         if (mp->id != id) {
00426                                 fprintf(stderr,
00427                                     "RACE! (%03lu stole lock %d from %03lu)\n",
00428                                     mp->id, lock, id);
00429                                 return ((void *)1);
00430                         }
00431                 }
00432 
00433                 /*
00434                  * Test self-blocking and unlocking by other threads/processes:
00435                  *
00436                  *      acquire the global lock
00437                  *      set our wakeup flag
00438                  *      release the global lock
00439                  *      acquire our per-thread lock
00440                  *
00441                  * The wakeup thread will wake us up.
00442                  */
00443                 if ((err = dbenv->mutex_lock(dbenv, gp->mutex)) != 0) {
00444                         fprintf(stderr,
00445                             "%03lu: global lock: %s\n", id, db_strerror(err));
00446                         return ((void *)1);
00447                 }
00448                 if (tp->id != 0 && tp->id != id) {
00449                         fprintf(stderr,
00450                     "%03lu: per-thread mutex isn't mine, owned by %03lu\n",
00451                             id, tp->id);
00452                         return ((void *)1);
00453                 }
00454                 tp->id = id;
00455                 if (verbose)
00456                         printf("%03lu: self-blocking (mtx: %lu)\n",
00457                             id, (u_long)tp->mutex);
00458                 if (tp->wakeme) {
00459                         fprintf(stderr,
00460                             "%03lu: wakeup flag incorrectly set\n", id);
00461                         return ((void *)1);
00462                 }
00463                 tp->wakeme = 1;
00464                 if ((err = dbenv->mutex_unlock(dbenv, gp->mutex)) != 0) {
00465                         fprintf(stderr,
00466                             "%03lu: global unlock: %s\n", id, db_strerror(err));
00467                         return ((void *)1);
00468                 }
00469                 if ((err = dbenv->mutex_lock(dbenv, tp->mutex)) != 0) {
00470                         fprintf(stderr, "%03lu: per-thread lock: %s\n",
00471                             id, db_strerror(err));
00472                         return ((void *)1);
00473                 }
00474                 /* Time passes... */
00475                 if (tp->wakeme) {
00476                         fprintf(stderr, "%03lu: wakeup flag not cleared\n", id);
00477                         return ((void *)1);
00478                 }
00479 
00480                 if (verbose)
00481                         printf("%03lu: release %d (mtx: %lu)\n",
00482                             id, lock, (u_long)mp->mutex);
00483 
00484                 /* Release the data lock. */
00485                 mp->id = 0;
00486                 if ((err = dbenv->mutex_unlock(dbenv, mp->mutex)) != 0) {
00487                         fprintf(stderr,
00488                             "%03lu: lock release: %s\n", id, db_strerror(err));
00489                         return ((void *)1);
00490                 }
00491 
00492                 if (--nl % 100 == 0) {
00493                         fprintf(stderr, "%03lu: %d\n", id, nl);
00494                         /*
00495                          * Windows buffers stderr and the output looks wrong
00496                          * without this.
00497                          */
00498                         fflush(stderr);
00499                 }
00500         }
00501 
00502         return (NULL);
00503 }
00504 
00505 int
00506 wakeup_start(id)
00507         u_long id;
00508 {
00509 #if defined(MUTEX_THREAD_TEST)
00510         int err;
00511 
00512         /*
00513          * Spawn off wakeup thread.
00514          */
00515         if ((err = os_thread_create(
00516             &wakep, NULL, run_wthread, (void *)id)) != 0) {
00517                 fprintf(stderr, "tm: failed spawning wakeup thread: %s\n",
00518                     db_strerror(err));
00519                 return (1);
00520         }
00521         return (0);
00522 #else
00523         return (run_wthread((void *)id) == NULL ? 0 : 1);
00524 #endif
00525 }
00526 
00527 int
00528 wakeup_wait()
00529 {
00530 #if defined(MUTEX_THREAD_TEST)
00531         void *retp;
00532 
00533         /*
00534          * A file is created when the wakeup thread is no longer needed.
00535          */
00536         os_thread_join(wakep, &retp);
00537         if (retp != NULL) {
00538                 fprintf(stderr, "tm: wakeup thread exited with error\n");
00539                 return (1);
00540         }
00541 #endif
00542         return (0);
00543 }
00544 
00545 /*
00546  * run_wthread --
00547  *      Thread to wake up other threads that are sleeping.
00548  */
00549 void *
00550 run_wthread(arg)
00551         void *arg;
00552 {
00553         TM *gp, *tp;
00554         u_long id, tid;
00555         int check_id, err;
00556 
00557         id = (uintptr_t)arg;
00558 #if defined(MUTEX_THREAD_TEST)
00559         tid = (u_long)os_thread_self();
00560 #else
00561         tid = 0;
00562 #endif
00563         printf("Wakeup: ID %03lu (PID: %lu; TID: %lx)\n",
00564             id, (u_long)getpid(), tid);
00565 
00566         gp = (TM *)gm_addr;
00567 
00568         /* Loop, waking up sleepers and periodically sleeping ourselves. */
00569         for (check_id = 0;; ++check_id) {
00570                 /* Check to see if the locking threads have finished. */
00571                 if (__os_exists(MT_FILE_QUIT, NULL) == 0)
00572                         break;
00573 
00574                 /* Check for ID wraparound. */
00575                 if (check_id == nthreads * nprocs)
00576                         check_id = 0;
00577 
00578                 /* Check for a thread that needs a wakeup. */
00579                 tp = (TM *)(tm_addr + check_id * sizeof(TM));
00580                 if (!tp->wakeme)
00581                         continue;
00582 
00583                 if (verbose) {
00584                         printf("%03lu: wakeup thread %03lu (mtx: %lu)\n",
00585                             id, tp->id, (u_long)tp->mutex);
00586                         fflush(stdout);
00587                 }
00588 
00589                 /* Acquire the global lock. */
00590                 if ((err = dbenv->mutex_lock(dbenv, gp->mutex)) != 0) {
00591                         fprintf(stderr,
00592                             "wakeup: global lock: %s\n", db_strerror(err));
00593                         return ((void *)1);
00594                 }
00595 
00596                 tp->wakeme = 0;
00597                 if ((err = dbenv->mutex_unlock(dbenv, tp->mutex)) != 0) {
00598                         fprintf(stderr,
00599                             "wakeup: unlock: %s\n", db_strerror(err));
00600                         return ((void *)1);
00601                 }
00602 
00603                 if ((err = dbenv->mutex_unlock(dbenv, gp->mutex))) {
00604                         fprintf(stderr,
00605                             "wakeup: global unlock: %s\n", db_strerror(err));
00606                         return ((void *)1);
00607                 }
00608 
00609                 __os_sleep(dbenv, 0, rand() % 3);
00610         }
00611         return (NULL);
00612 }
00613 
00614 /*
00615  * tm_env_init --
00616  *      Create the backing database environment.
00617  */
00618 int
00619 tm_env_init()
00620 {
00621         u_int32_t flags;
00622         int ret;
00623         char *home;
00624 
00625         /*
00626          * Create an environment object and initialize it for error
00627          * reporting.
00628          */
00629         if ((ret = db_env_create(&dbenv, 0)) != 0) {
00630                 fprintf(stderr, "tm: %s\n", db_strerror(ret));
00631                 return (1);
00632         }
00633         dbenv->set_errfile(dbenv, stderr);
00634         dbenv->set_errpfx(dbenv, "tm");
00635 
00636         /* Allocate enough mutexes. */
00637         if ((ret = dbenv->mutex_set_increment(dbenv,
00638             1 + nthreads * nprocs + maxlocks)) != 0) {
00639                 dbenv->err(dbenv, ret, "dbenv->mutex_set_increment");
00640                 return (1);
00641         }
00642 
00643         flags = DB_CREATE;
00644         if (nprocs == 1) {
00645                 home = NULL;
00646                 flags |= DB_PRIVATE;
00647         } else
00648                 home = TESTDIR;
00649         if (nthreads != 1)
00650                 flags |= DB_THREAD;
00651         if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0) {
00652                 dbenv->err(dbenv, ret, "environment open: %s", home);
00653                 return (1);
00654         }
00655 
00656         return (0);
00657 }
00658 
00659 /*
00660  * tm_env_close --
00661  *      Close the backing database environment.
00662  */
00663 void
00664 tm_env_close()
00665 {
00666         (void)dbenv->close(dbenv, 0);
00667 }
00668 
00669 /*
00670  * tm_file_init --
00671  *      Initialize the backing file.
00672  */
00673 void
00674 tm_file_init()
00675 {
00676         DB_FH *fhp;
00677         int err;
00678         size_t nwrite;
00679 
00680         /* Initialize the backing file. */
00681         if (verbose)
00682                 printf("Create the backing file.\n");
00683 
00684         (void)unlink(MT_FILE);
00685 
00686         if ((err = __os_open(dbenv, MT_FILE,
00687             DB_OSO_CREATE | DB_OSO_TRUNC, 0666, &fhp)) == -1) {
00688                 (void)fprintf(stderr,
00689                     "%s: open: %s\n", MT_FILE, db_strerror(err));
00690                 exit(EXIT_FAILURE);
00691         }
00692 
00693         if ((err = __os_seek(dbenv, fhp,
00694             0, 0, len, 0, DB_OS_SEEK_SET)) != 0 ||
00695             (err = __os_write(dbenv, fhp, &err, 1, &nwrite)) != 0 ||
00696             nwrite != 1) {
00697                 (void)fprintf(stderr,
00698                     "%s: seek/write: %s\n", MT_FILE, db_strerror(err));
00699                 exit(EXIT_FAILURE);
00700         }
00701         (void)__os_closehandle(dbenv, fhp);
00702 }
00703 
00704 /*
00705  * tm_mutex_init --
00706  *      Initialize the mutexes.
00707  */
00708 void
00709 tm_mutex_init()
00710 {
00711         TM *mp;
00712         int err, i;
00713 
00714         if (verbose)
00715                 printf("Allocate the global mutex: ");
00716         mp = (TM *)gm_addr;
00717         if ((err = dbenv->mutex_alloc(dbenv, 0, &mp->mutex)) != 0) {
00718                 fprintf(stderr,
00719                     "DB_ENV->mutex_alloc (global): %s\n", db_strerror(err));
00720                 exit(EXIT_FAILURE);
00721         }
00722         if (verbose)
00723                 printf("%lu\n", (u_long)mp->mutex);
00724 
00725         if (verbose)
00726                 printf(
00727                     "Allocate %d per-thread, self-blocking mutexes: ",
00728                     nthreads * nprocs);
00729         for (i = 0; i < nthreads * nprocs; ++i) {
00730                 mp = (TM *)(tm_addr + i * sizeof(TM));
00731                 if ((err = dbenv->mutex_alloc(
00732                     dbenv, DB_MUTEX_SELF_BLOCK, &mp->mutex)) != 0) {
00733                         fprintf(stderr,
00734                             "DB_ENV->mutex_alloc (per-thread %d): %s\n",
00735                             i, db_strerror(err));
00736                         exit(EXIT_FAILURE);
00737                 }
00738                 if ((err = dbenv->mutex_lock(dbenv, mp->mutex)) != 0) {
00739                         fprintf(stderr,
00740                             "DB_ENV->mutex_lock (per-thread %d): %s\n",
00741                             i, db_strerror(err));
00742                         exit(EXIT_FAILURE);
00743                 }
00744                 if (verbose)
00745                         printf("%lu ", (u_long)mp->mutex);
00746         }
00747         if (verbose)
00748                 printf("\n");
00749 
00750         if (verbose)
00751                 printf("Allocate %d per-lock mutexes: ", maxlocks);
00752         for (i = 0; i < maxlocks; ++i) {
00753                 mp = (TM *)(lm_addr + i * sizeof(TM));
00754                 if ((err = dbenv->mutex_alloc(dbenv, 0, &mp->mutex)) != 0) {
00755                         fprintf(stderr,
00756                             "DB_ENV->mutex_alloc (per-lock: %d): %s\n",
00757                             i, db_strerror(err));
00758                         exit(EXIT_FAILURE);
00759                 }
00760                 if (verbose)
00761                         printf("%lu ", (u_long)mp->mutex);
00762         }
00763         if (verbose)
00764                 printf("\n");
00765 }
00766 
00767 /*
00768  * tm_mutex_destroy --
00769  *      Destroy the mutexes.
00770  */
00771 void
00772 tm_mutex_destroy()
00773 {
00774         TM *gp, *mp;
00775         int err, i;
00776 
00777         if (verbose)
00778                 printf("Destroy the global mutex.\n");
00779         gp = (TM *)gm_addr;
00780         if ((err = dbenv->mutex_free(dbenv, gp->mutex)) != 0) {
00781                 fprintf(stderr,
00782                     "DB_ENV->mutex_free (global): %s\n", db_strerror(err));
00783                 exit(EXIT_FAILURE);
00784         }
00785 
00786         if (verbose)
00787                 printf("Destroy the per-thread mutexes.\n");
00788         for (i = 0; i < nthreads * nprocs; ++i) {
00789                 mp = (TM *)(tm_addr + i * sizeof(TM));
00790                 if ((err = dbenv->mutex_free(dbenv, mp->mutex)) != 0) {
00791                         fprintf(stderr,
00792                             "DB_ENV->mutex_free (per-thread %d): %s\n",
00793                             i, db_strerror(err));
00794                         exit(EXIT_FAILURE);
00795                 }
00796         }
00797 
00798         if (verbose)
00799                 printf("Destroy the per-lock mutexes.\n");
00800         for (i = 0; i < maxlocks; ++i) {
00801                 mp = (TM *)(lm_addr + i * sizeof(TM));
00802                 if ((err = dbenv->mutex_free(dbenv, mp->mutex)) != 0) {
00803                         fprintf(stderr,
00804                             "DB_ENV->mutex_free (per-lock: %d): %s\n",
00805                             i, db_strerror(err));
00806                         exit(EXIT_FAILURE);
00807                 }
00808         }
00809 
00810         (void)unlink(MT_FILE);
00811 }
00812 
00813 /*
00814  * tm_mutex_stats --
00815  *      Display mutex statistics.
00816  */
00817 void
00818 tm_mutex_stats()
00819 {
00820 #ifdef HAVE_STATISTICS
00821         TM *mp;
00822         int i;
00823         u_int32_t set_wait, set_nowait;
00824 
00825         printf("Per-lock mutex statistics.\n");
00826         for (i = 0; i < maxlocks; ++i) {
00827                 mp = (TM *)(lm_addr + i * sizeof(TM));
00828                 __mutex_set_wait_info(dbenv, mp->mutex, &set_wait, &set_nowait);
00829                 printf("mutex %2d: wait: %lu; no wait %lu\n", i,
00830                     (u_long)set_wait, (u_long)set_nowait);
00831         }
00832 #endif
00833 }
00834 
00835 /*
00836  * map_file --
00837  *      Map in the backing file.
00838  */
00839 void
00840 map_file(gm_addrp, tm_addrp, lm_addrp, fhpp)
00841         u_int8_t **gm_addrp, **tm_addrp, **lm_addrp;
00842         DB_FH **fhpp;
00843 {
00844         void *addr;
00845         DB_FH *fhp;
00846         int err;
00847 
00848 #ifndef MAP_FAILED
00849 #define MAP_FAILED      (void *)-1
00850 #endif
00851 #ifndef MAP_FILE
00852 #define MAP_FILE        0
00853 #endif
00854         if ((err = __os_open(dbenv, MT_FILE, 0, 0, &fhp)) != 0) {
00855                 fprintf(stderr, "%s: open %s\n", MT_FILE, db_strerror(err));
00856                 exit(EXIT_FAILURE);
00857         }
00858 
00859         if ((err = __os_mapfile(dbenv, MT_FILE, fhp, len, 0, &addr)) != 0) {
00860                 fprintf(stderr, "%s: mmap: %s\n", MT_FILE, db_strerror(err));
00861                 exit(EXIT_FAILURE);
00862         }
00863 
00864         *gm_addrp = (u_int8_t *)addr;
00865         addr = (u_int8_t *)addr + sizeof(TM);
00866         *tm_addrp = (u_int8_t *)addr;
00867         addr = (u_int8_t *)addr + sizeof(TM) * (nthreads * nprocs);
00868         *lm_addrp = (u_int8_t *)addr;
00869 
00870         if (fhpp != NULL)
00871                 *fhpp = fhp;
00872 }
00873 
00874 /*
00875  * unmap_file --
00876  *      Discard backing file map.
00877  */
00878 void
00879 unmap_file(addr, fhp)
00880         u_int8_t *addr;
00881         DB_FH *fhp;
00882 {
00883         int err;
00884 
00885         if ((err = __os_unmapfile(dbenv, addr, len)) != 0) {
00886                 fprintf(stderr, "munmap: %s\n", db_strerror(err));
00887                 exit(EXIT_FAILURE);
00888         }
00889         if ((err = __os_closehandle(dbenv, fhp)) != 0) {
00890                 fprintf(stderr, "close: %s\n", db_strerror(err));
00891                 exit(EXIT_FAILURE);
00892         }
00893 }
00894 
00895 /*
00896  * usage --
00897  *
00898  */
00899 int
00900 usage()
00901 {
00902         (void)fprintf(stderr, "%s\n\t%s\n",
00903             "usage: tm [-v] [-l maxlocks]",
00904             "[-n locks] [-p procs] [-T locker=ID|wakeup=ID] [-t threads]");
00905         return (EXIT_FAILURE);
00906 }
00907 
00908 /*
00909  * os_wait --
00910  *      Wait for an array of N procs.
00911  */
00912 int
00913 os_wait(procs, nprocs)
00914         os_pid_t *procs;
00915         int nprocs;
00916 {
00917         int i, status;
00918 #if defined(DB_WIN32)
00919         DWORD ret;
00920 #endif
00921 
00922         status = 0;
00923 
00924 #if defined(DB_WIN32)
00925         do {
00926                 ret = WaitForMultipleObjects(nprocs, procs, FALSE, INFINITE);
00927                 i = ret - WAIT_OBJECT_0;
00928                 if (i < 0 || i >= nprocs)
00929                         return (__os_get_errno());
00930 
00931                 if ((GetExitCodeProcess(procs[i], &ret) == 0) || (ret != 0))
00932                         return (ret);
00933 
00934                 /* remove the process handle from the list */
00935                 while (++i < nprocs)
00936                         procs[i - 1] = procs[i];
00937         } while (--nprocs);
00938 #elif !defined(HAVE_VXWORKS)
00939         do {
00940                 if ((i = wait(&status)) == -1)
00941                         return (__os_get_errno());
00942 
00943                 if (WIFEXITED(status) == 0 || WEXITSTATUS(status) != 0) {
00944                         for (i = 0; i < nprocs; i++)
00945                                 kill(procs[i], SIGKILL);
00946                         return (WEXITSTATUS(status));
00947                 }
00948         } while (--nprocs);
00949 #endif
00950 
00951         return (0);
00952 }
00953 
00954 os_pid_t
00955 spawn_proc(id, tmpath, typearg)
00956         u_long id;
00957         char *tmpath, *typearg;
00958 {
00959         char lbuf[16], nbuf[16], pbuf[16], tbuf[16], Tbuf[256];
00960         char *const vbuf = verbose ?  "-v" : NULL;
00961         char *args[] = { NULL /* tmpath */,
00962             "-l", NULL /* lbuf */, "-n", NULL /* nbuf */,
00963             "-p", NULL /* pbuf */, "-t", NULL /* tbuf */,
00964             "-T", NULL /* Tbuf */, NULL /* vbuf */,
00965             NULL
00966         };
00967 
00968         args[0] = tmpath;
00969         snprintf(lbuf, sizeof(lbuf),  "%d", maxlocks);
00970         args[2] = lbuf;
00971         snprintf(nbuf, sizeof(nbuf),  "%d", nlocks);
00972         args[4] = nbuf;
00973         snprintf(pbuf, sizeof(pbuf),  "%d", nprocs);
00974         args[6] = pbuf;
00975         snprintf(tbuf, sizeof(tbuf),  "%d", nthreads);
00976         args[8] = tbuf;
00977         snprintf(Tbuf, sizeof(Tbuf),  "%s=%lu", typearg, id);
00978         args[10] = Tbuf;
00979         args[11] = vbuf;
00980 
00981         return (os_spawn(tmpath, args));
00982 }
00983 
00984 os_pid_t
00985 os_spawn(path, argv)
00986         const char *path;
00987         char *const argv[];
00988 {
00989         os_pid_t pid;
00990         int status;
00991 
00992         COMPQUIET(pid, 0);
00993         COMPQUIET(status, 0);
00994 
00995 #ifdef HAVE_VXWORKS
00996         fprintf(stderr, "ERROR: os_spawn not supported for VxWorks.\n");
00997         return (OS_BAD_PID);
00998 #elif defined(HAVE_QNX)
00999         /*
01000          * For QNX, we cannot fork if we've ever used threads.  So
01001          * we'll use their spawn function.  We use 'spawnl' which
01002          * is NOT a POSIX function.
01003          *
01004          * The return value of spawnl is just what we want depending
01005          * on the value of the 'wait' arg.
01006          */
01007         return (spawnv(P_NOWAIT, path, argv));
01008 #elif defined(DB_WIN32)
01009         return (os_pid_t)(_spawnv(P_NOWAIT, path, argv));
01010 #else
01011         if ((pid = fork()) != 0) {
01012                 if (pid == -1)
01013                         return (OS_BAD_PID);
01014                 return (pid);
01015         } else {
01016                 execv(path, argv);
01017                 exit(EXIT_FAILURE);
01018         }
01019 #endif
01020 }

Generated on Sun Dec 25 12:14:41 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2