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

lock_region.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: lock_region.c,v 12.6 2005/10/07 20:21:31 ubell Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 
00015 #include <string.h>
00016 #endif
00017 
00018 #include "db_int.h"
00019 #include "dbinc/db_shash.h"
00020 #include "dbinc/lock.h"
00021 
00022 static int  __lock_region_init __P((DB_ENV *, DB_LOCKTAB *));
00023 static size_t
00024             __lock_region_size __P((DB_ENV *));
00025 
00026 /*
00027  * The conflict arrays are set up such that the row is the lock you are
00028  * holding and the column is the lock that is desired.
00029  */
00030 #define DB_LOCK_RIW_N   9
00031 static const u_int8_t db_riw_conflicts[] = {
00032 /*         N   R   W   WT  IW  IR  RIW DR  WW */
00033 /*   N */  0,  0,  0,  0,  0,  0,  0,  0,  0,
00034 /*   R */  0,  0,  1,  0,  1,  0,  1,  0,  1,
00035 /*   W */  0,  1,  1,  1,  1,  1,  1,  1,  1,
00036 /*  WT */  0,  0,  0,  0,  0,  0,  0,  0,  0,
00037 /*  IW */  0,  1,  1,  0,  0,  0,  0,  1,  1,
00038 /*  IR */  0,  0,  1,  0,  0,  0,  0,  0,  1,
00039 /* RIW */  0,  1,  1,  0,  0,  0,  0,  1,  1,
00040 /*  DR */  0,  0,  1,  0,  1,  0,  1,  0,  0,
00041 /*  WW */  0,  1,  1,  0,  1,  1,  1,  0,  1
00042 };
00043 
00044 /*
00045  * This conflict array is used for concurrent db access (CDB).  It uses
00046  * the same locks as the db_riw_conflicts array, but adds an IW mode to
00047  * be used for write cursors.
00048  */
00049 #define DB_LOCK_CDB_N   5
00050 static const u_int8_t db_cdb_conflicts[] = {
00051         /*              N       R       W       WT      IW */
00052         /*   N */       0,      0,      0,      0,      0,
00053         /*   R */       0,      0,      1,      0,      0,
00054         /*   W */       0,      1,      1,      1,      1,
00055         /*  WT */       0,      0,      0,      0,      0,
00056         /*  IW */       0,      0,      1,      0,      1
00057 };
00058 
00059 /*
00060  * __lock_open --
00061  *      Internal version of lock_open: only called from DB_ENV->open.
00062  *
00063  * PUBLIC: int __lock_open __P((DB_ENV *));
00064  */
00065 int
00066 __lock_open(dbenv)
00067         DB_ENV *dbenv;
00068 {
00069         DB_LOCKREGION *region;
00070         DB_LOCKTAB *lt;
00071         size_t size;
00072         int region_locked, ret;
00073 
00074         region_locked = 0;
00075 
00076         /* Create the lock table structure. */
00077         if ((ret = __os_calloc(dbenv, 1, sizeof(DB_LOCKTAB), &lt)) != 0)
00078                 return (ret);
00079         lt->dbenv = dbenv;
00080 
00081         /* Join/create the lock region. */
00082         lt->reginfo.dbenv = dbenv;
00083         lt->reginfo.type = REGION_TYPE_LOCK;
00084         lt->reginfo.id = INVALID_REGION_ID;
00085         lt->reginfo.flags = REGION_JOIN_OK;
00086         if (F_ISSET(dbenv, DB_ENV_CREATE))
00087                 F_SET(&lt->reginfo, REGION_CREATE_OK);
00088         size = __lock_region_size(dbenv);
00089         if ((ret = __db_r_attach(dbenv, &lt->reginfo, size)) != 0)
00090                 goto err;
00091 
00092         /* If we created the region, initialize it. */
00093         if (F_ISSET(&lt->reginfo, REGION_CREATE))
00094                 if ((ret = __lock_region_init(dbenv, lt)) != 0)
00095                         goto err;
00096 
00097         /* Set the local addresses. */
00098         region = lt->reginfo.primary =
00099             R_ADDR(&lt->reginfo, lt->reginfo.rp->primary);
00100 
00101         /* Set remaining pointers into region. */
00102         lt->conflicts = R_ADDR(&lt->reginfo, region->conf_off);
00103         lt->obj_tab = R_ADDR(&lt->reginfo, region->obj_off);
00104         lt->locker_tab = R_ADDR(&lt->reginfo, region->locker_off);
00105 
00106         dbenv->lk_handle = lt;
00107 
00108         LOCK_SYSTEM_LOCK(dbenv);
00109         region_locked = 1;
00110 
00111         if (dbenv->lk_detect != DB_LOCK_NORUN) {
00112                 /*
00113                  * Check for incompatible automatic deadlock detection requests.
00114                  * There are scenarios where changing the detector configuration
00115                  * is reasonable, but we disallow them guessing it is likely to
00116                  * be an application error.
00117                  *
00118                  * We allow applications to turn on the lock detector, and we
00119                  * ignore attempts to set it to the default or current value.
00120                  */
00121                 if (region->detect != DB_LOCK_NORUN &&
00122                     dbenv->lk_detect != DB_LOCK_DEFAULT &&
00123                     region->detect != dbenv->lk_detect) {
00124                         __db_err(dbenv,
00125                     "lock_open: incompatible deadlock detector mode");
00126                         ret = EINVAL;
00127                         goto err;
00128                 }
00129                 if (region->detect == DB_LOCK_NORUN)
00130                         region->detect = dbenv->lk_detect;
00131         }
00132 
00133         /*
00134          * A process joining the region may have reset the lock and transaction
00135          * timeouts.
00136          */
00137         if (dbenv->lk_timeout != 0)
00138                 region->lk_timeout = dbenv->lk_timeout;
00139         if (dbenv->tx_timeout != 0)
00140                 region->tx_timeout = dbenv->tx_timeout;
00141 
00142         LOCK_SYSTEM_UNLOCK(dbenv);
00143         region_locked = 0;
00144 
00145         return (0);
00146 
00147 err:    dbenv->lk_handle = NULL;
00148         if (lt->reginfo.addr != NULL) {
00149                 if (region_locked)
00150                         LOCK_SYSTEM_UNLOCK(dbenv);
00151                 (void)__db_r_detach(dbenv, &lt->reginfo, 0);
00152         }
00153 
00154         __os_free(dbenv, lt);
00155         return (ret);
00156 }
00157 
00158 /*
00159  * __lock_region_init --
00160  *      Initialize the lock region.
00161  */
00162 static int
00163 __lock_region_init(dbenv, lt)
00164         DB_ENV *dbenv;
00165         DB_LOCKTAB *lt;
00166 {
00167         const u_int8_t *lk_conflicts;
00168         struct __db_lock *lp;
00169         DB_LOCKER *lidp;
00170         DB_LOCKOBJ *op;
00171         DB_LOCKREGION *region;
00172         u_int32_t i;
00173         u_int8_t *addr;
00174         int lk_modes, ret;
00175 
00176         if ((ret = __db_shalloc(&lt->reginfo,
00177             sizeof(DB_LOCKREGION), 0, &lt->reginfo.primary)) != 0)
00178                 goto mem_err;
00179         lt->reginfo.rp->primary = R_OFFSET(&lt->reginfo, lt->reginfo.primary);
00180         region = lt->reginfo.primary;
00181         memset(region, 0, sizeof(*region));
00182 
00183         if ((ret = __mutex_alloc(
00184             dbenv, MTX_LOCK_REGION, 0, &region->mtx_region)) != 0)
00185                 return (ret);
00186 
00187         /* Select a conflict matrix if none specified. */
00188         if (dbenv->lk_modes == 0)
00189                 if (CDB_LOCKING(dbenv)) {
00190                         lk_modes = DB_LOCK_CDB_N;
00191                         lk_conflicts = db_cdb_conflicts;
00192                 } else {
00193                         lk_modes = DB_LOCK_RIW_N;
00194                         lk_conflicts = db_riw_conflicts;
00195                 }
00196         else {
00197                 lk_modes = dbenv->lk_modes;
00198                 lk_conflicts = dbenv->lk_conflicts;
00199         }
00200 
00201         region->need_dd = 0;
00202         LOCK_SET_TIME_INVALID(&region->next_timeout);
00203         region->detect = DB_LOCK_NORUN;
00204         region->lk_timeout = dbenv->lk_timeout;
00205         region->tx_timeout = dbenv->tx_timeout;
00206         region->locker_t_size = __db_tablesize(dbenv->lk_max_lockers);
00207         region->object_t_size = __db_tablesize(dbenv->lk_max_objects);
00208         memset(&region->stat, 0, sizeof(region->stat));
00209         region->stat.st_id = 0;
00210         region->stat.st_cur_maxid = DB_LOCK_MAXID;
00211         region->stat.st_maxlocks = dbenv->lk_max;
00212         region->stat.st_maxlockers = dbenv->lk_max_lockers;
00213         region->stat.st_maxobjects = dbenv->lk_max_objects;
00214         region->stat.st_nmodes = lk_modes;
00215 
00216         /* Allocate room for the conflict matrix and initialize it. */
00217         if ((ret = __db_shalloc(
00218             &lt->reginfo, (size_t)(lk_modes * lk_modes), 0, &addr)) != 0)
00219                 goto mem_err;
00220         memcpy(addr, lk_conflicts, (size_t)(lk_modes * lk_modes));
00221         region->conf_off = R_OFFSET(&lt->reginfo, addr);
00222 
00223         /* Allocate room for the object hash table and initialize it. */
00224         if ((ret = __db_shalloc(&lt->reginfo,
00225             region->object_t_size * sizeof(DB_HASHTAB), 0, &addr)) != 0)
00226                 goto mem_err;
00227         __db_hashinit(addr, region->object_t_size);
00228         region->obj_off = R_OFFSET(&lt->reginfo, addr);
00229 
00230         /* Allocate room for the locker hash table and initialize it. */
00231         if ((ret = __db_shalloc(&lt->reginfo,
00232             region->locker_t_size * sizeof(DB_HASHTAB), 0, &addr)) != 0)
00233                 goto mem_err;
00234         __db_hashinit(addr, region->locker_t_size);
00235         region->locker_off = R_OFFSET(&lt->reginfo, addr);
00236 
00237         /* Initialize locks onto a free list. */
00238         SH_TAILQ_INIT(&region->free_locks);
00239         for (i = 0; i < region->stat.st_maxlocks; ++i) {
00240                 if ((ret = __db_shalloc(&lt->reginfo,
00241                     sizeof(struct __db_lock), 0, &lp)) != 0)
00242                         goto mem_err;
00243                 lp->mtx_lock = MUTEX_INVALID;
00244                 lp->gen = 0;
00245                 lp->status = DB_LSTAT_FREE;
00246                 SH_TAILQ_INSERT_HEAD(&region->free_locks, lp, links, __db_lock);
00247         }
00248 
00249         /* Initialize objects onto a free list.  */
00250         SH_TAILQ_INIT(&region->dd_objs);
00251         SH_TAILQ_INIT(&region->free_objs);
00252         for (i = 0; i < region->stat.st_maxobjects; ++i) {
00253                 if ((ret = __db_shalloc(&lt->reginfo,
00254                     sizeof(DB_LOCKOBJ), 0, &op)) != 0)
00255                         goto mem_err;
00256                 SH_TAILQ_INSERT_HEAD(
00257                     &region->free_objs, op, links, __db_lockobj);
00258         }
00259 
00260         /* Initialize lockers onto a free list.  */
00261         SH_TAILQ_INIT(&region->lockers);
00262         SH_TAILQ_INIT(&region->free_lockers);
00263         for (i = 0; i < region->stat.st_maxlockers; ++i) {
00264                 if ((ret = __db_shalloc(&lt->reginfo,
00265                     sizeof(DB_LOCKER), 0, &lidp)) != 0) {
00266 mem_err:                __db_err(dbenv,
00267                             "Unable to allocate memory for the lock table");
00268                         return (ret);
00269                 }
00270                 SH_TAILQ_INSERT_HEAD(
00271                     &region->free_lockers, lidp, links, __db_locker);
00272         }
00273 
00274         return (0);
00275 }
00276 
00277 /*
00278  * __lock_dbenv_refresh --
00279  *      Clean up after the lock system on a close or failed open.
00280  *
00281  * PUBLIC: int __lock_dbenv_refresh __P((DB_ENV *));
00282  */
00283 int
00284 __lock_dbenv_refresh(dbenv)
00285         DB_ENV *dbenv;
00286 {
00287         struct __db_lock *lp;
00288         DB_LOCKER *locker;
00289         DB_LOCKOBJ *lockobj;
00290         DB_LOCKREGION *lr;
00291         DB_LOCKTAB *lt;
00292         REGINFO *reginfo;
00293         int ret;
00294 
00295         lt = dbenv->lk_handle;
00296         reginfo = &lt->reginfo;
00297         lr = reginfo->primary;
00298 
00299         /*
00300          * If a private region, return the memory to the heap.  Not needed for
00301          * filesystem-backed or system shared memory regions, that memory isn't
00302          * owned by any particular process.
00303          */
00304         if (F_ISSET(dbenv, DB_ENV_PRIVATE)) {
00305                 /* Discard the conflict matrix. */
00306                 __db_shalloc_free(reginfo, R_ADDR(reginfo, lr->conf_off));
00307 
00308                 /* Discard the object hash table. */
00309                 __db_shalloc_free(reginfo, R_ADDR(reginfo, lr->obj_off));
00310 
00311                 /* Discard the locker hash table. */
00312                 __db_shalloc_free(reginfo, R_ADDR(reginfo, lr->locker_off));
00313 
00314                 /* Discard locks. */
00315                 while ((lp =
00316                     SH_TAILQ_FIRST(&lr->free_locks, __db_lock)) != NULL) {
00317                         SH_TAILQ_REMOVE(&lr->free_locks, lp, links, __db_lock);
00318                         __db_shalloc_free(reginfo, lp);
00319                 }
00320 
00321                 /* Discard objects. */
00322                 while ((lockobj =
00323                     SH_TAILQ_FIRST(&lr->free_objs, __db_lockobj)) != NULL) {
00324                         SH_TAILQ_REMOVE(
00325                             &lr->free_objs, lockobj, links, __db_lockobj);
00326                         __db_shalloc_free(reginfo, lockobj);
00327                 }
00328 
00329                 /* Discard lockers. */
00330                 while ((locker =
00331                     SH_TAILQ_FIRST(&lr->free_lockers, __db_locker)) != NULL) {
00332                         SH_TAILQ_REMOVE(
00333                             &lr->free_lockers, locker, links, __db_locker);
00334                         __db_shalloc_free(reginfo, locker);
00335                 }
00336         }
00337 
00338         /* Detach from the region. */
00339         ret = __db_r_detach(dbenv, reginfo, 0);
00340 
00341         /* Discard DB_LOCKTAB. */
00342         __os_free(dbenv, lt);
00343         dbenv->lk_handle = NULL;
00344 
00345         return (ret);
00346 }
00347 
00348 /*
00349  * __lock_region_mutex_count --
00350  *      Return the number of mutexes the lock region will need.
00351  *
00352  * PUBLIC: u_int32_t __lock_region_mutex_count __P((DB_ENV *));
00353  */
00354 u_int32_t
00355 __lock_region_mutex_count(dbenv)
00356         DB_ENV *dbenv;
00357 {
00358         return (dbenv->lk_max);
00359 }
00360 
00361 /*
00362  * __lock_region_size --
00363  *      Return the region size.
00364  */
00365 static size_t
00366 __lock_region_size(dbenv)
00367         DB_ENV *dbenv;
00368 {
00369         size_t retval;
00370 
00371         /*
00372          * Figure out how much space we're going to need.  This list should
00373          * map one-to-one with the __db_shalloc calls in __lock_region_init.
00374          */
00375         retval = 0;
00376         retval += __db_shalloc_size(sizeof(DB_LOCKREGION), 0);
00377         retval += __db_shalloc_size(
00378             (size_t)(dbenv->lk_modes * dbenv->lk_modes), 0);
00379         retval += __db_shalloc_size(
00380             __db_tablesize(dbenv->lk_max_objects) * (sizeof(DB_HASHTAB)), 0);
00381         retval += __db_shalloc_size(
00382             __db_tablesize(dbenv->lk_max_lockers) * (sizeof(DB_HASHTAB)), 0);
00383         retval +=
00384             __db_shalloc_size(sizeof(struct __db_lock), 0) * dbenv->lk_max;
00385         retval +=
00386             __db_shalloc_size(sizeof(DB_LOCKOBJ), 0) * dbenv->lk_max_objects;
00387         retval +=
00388             __db_shalloc_size(sizeof(DB_LOCKER), 0) * dbenv->lk_max_lockers;
00389 
00390         /*
00391          * Include 16 bytes of string space per lock.  DB doesn't use it
00392          * because we pre-allocate lock space for DBTs in the structure.
00393          */
00394         retval += __db_shalloc_size(dbenv->lk_max * 16, sizeof(size_t));
00395 
00396         /* And we keep getting this wrong, let's be generous. */
00397         retval += retval / 4;
00398 
00399         return (retval);
00400 }

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