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

lock_id.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_id.c,v 12.10 2005/10/14 15:15:16 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 #include <stdlib.h>
00017 #endif
00018 
00019 #include "db_int.h"
00020 #include "dbinc/db_shash.h"
00021 #include "dbinc/lock.h"
00022 #include "dbinc/log.h"
00023 
00024 /*
00025  * __lock_id_pp --
00026  *      DB_ENV->lock_id pre/post processing.
00027  *
00028  * PUBLIC: int __lock_id_pp __P((DB_ENV *, u_int32_t *));
00029  */
00030 int
00031 __lock_id_pp(dbenv, idp)
00032         DB_ENV *dbenv;
00033         u_int32_t *idp;
00034 {
00035         DB_THREAD_INFO *ip;
00036         int ret;
00037 
00038         PANIC_CHECK(dbenv);
00039         ENV_REQUIRES_CONFIG(dbenv,
00040             dbenv->lk_handle, "DB_ENV->lock_id", DB_INIT_LOCK);
00041 
00042         ENV_ENTER(dbenv, ip);
00043         REPLICATION_WRAP(dbenv, (__lock_id(dbenv, idp, NULL)), ret);
00044         ENV_LEAVE(dbenv, ip);
00045         return (ret);
00046 }
00047 
00048 /*
00049  * __lock_id --
00050  *      DB_ENV->lock_id.
00051  *
00052  * PUBLIC: int  __lock_id __P((DB_ENV *, u_int32_t *, DB_LOCKER **));
00053  */
00054 int
00055 __lock_id(dbenv, idp, lkp)
00056         DB_ENV *dbenv;
00057         u_int32_t *idp;
00058         DB_LOCKER **lkp;
00059 {
00060         DB_LOCKER *lk;
00061         DB_LOCKTAB *lt;
00062         DB_LOCKREGION *region;
00063         u_int32_t id, *ids, locker_ndx;
00064         int nids, ret;
00065 
00066         lt = dbenv->lk_handle;
00067         region = lt->reginfo.primary;
00068         ret = 0;
00069 
00070         id = DB_LOCK_INVALIDID;
00071         lk = NULL;
00072 
00073         LOCK_SYSTEM_LOCK(dbenv);
00074 
00075         /*
00076          * Allocate a new lock id.  If we wrap around then we find the minimum
00077          * currently in use and make sure we can stay below that.  This code is
00078          * similar to code in __txn_begin_int for recovering txn ids.
00079          *
00080          * Our current valid range can span the maximum valid value, so check
00081          * for it and wrap manually.
00082          */
00083         if (region->stat.st_id == DB_LOCK_MAXID &&
00084             region->stat.st_cur_maxid != DB_LOCK_MAXID)
00085                 region->stat.st_id = DB_LOCK_INVALIDID;
00086         if (region->stat.st_id == region->stat.st_cur_maxid) {
00087                 if ((ret = __os_malloc(dbenv,
00088                     sizeof(u_int32_t) * region->stat.st_nlockers, &ids)) != 0)
00089                         goto err;
00090                 nids = 0;
00091                 for (lk = SH_TAILQ_FIRST(&region->lockers, __db_locker);
00092                     lk != NULL;
00093                     lk = SH_TAILQ_NEXT(lk, ulinks, __db_locker))
00094                         ids[nids++] = lk->id;
00095                 region->stat.st_id = DB_LOCK_INVALIDID;
00096                 region->stat.st_cur_maxid = DB_LOCK_MAXID;
00097                 if (nids != 0)
00098                         __db_idspace(ids, nids,
00099                             &region->stat.st_id, &region->stat.st_cur_maxid);
00100                 __os_free(dbenv, ids);
00101         }
00102         id = ++region->stat.st_id;
00103 
00104         /* Allocate a locker for this id. */
00105         LOCKER_LOCK(lt, region, id, locker_ndx);
00106         ret = __lock_getlocker(lt, id, locker_ndx, 1, &lk);
00107 
00108 err:    LOCK_SYSTEM_UNLOCK(dbenv);
00109 
00110         if (idp)
00111                 *idp = id;
00112         if (lkp)
00113                 *lkp = lk;
00114         return (ret);
00115 }
00116 
00117 /*
00118  * __lock_set_thread_id --
00119  *      Set the thread_id in an existing locker.
00120  * PUBLIC: void __lock_set_thread_id __P((DB_LOCKER *, pid_t, db_threadid_t));
00121  */
00122 void
00123 __lock_set_thread_id(lref, pid, tid)
00124         DB_LOCKER *lref;
00125         pid_t pid;
00126         db_threadid_t tid;
00127 {
00128         lref->pid = pid;
00129         lref->tid = tid;
00130 }
00131 
00132 /*
00133  * __lock_id_free_pp --
00134  *      DB_ENV->lock_id_free pre/post processing.
00135  *
00136  * PUBLIC: int __lock_id_free_pp __P((DB_ENV *, u_int32_t));
00137  */
00138 int
00139 __lock_id_free_pp(dbenv, id)
00140         DB_ENV *dbenv;
00141         u_int32_t id;
00142 {
00143         DB_THREAD_INFO *ip;
00144         int ret;
00145 
00146         PANIC_CHECK(dbenv);
00147         ENV_REQUIRES_CONFIG(dbenv,
00148             dbenv->lk_handle, "DB_ENV->lock_id_free", DB_INIT_LOCK);
00149 
00150         ENV_ENTER(dbenv, ip);
00151         REPLICATION_WRAP(dbenv, (__lock_id_free(dbenv, id)), ret);
00152         ENV_LEAVE(dbenv, ip);
00153         return (ret);
00154 }
00155 
00156 /*
00157  * __lock_id_free --
00158  *      Free a locker id.
00159  *
00160  * PUBLIC: int  __lock_id_free __P((DB_ENV *, u_int32_t));
00161  */
00162 int
00163 __lock_id_free(dbenv, id)
00164         DB_ENV *dbenv;
00165         u_int32_t id;
00166 {
00167         DB_LOCKER *sh_locker;
00168         DB_LOCKTAB *lt;
00169         DB_LOCKREGION *region;
00170         u_int32_t locker_ndx;
00171         int ret;
00172 
00173         PANIC_CHECK(dbenv);
00174         ENV_REQUIRES_CONFIG(dbenv,
00175             dbenv->lk_handle, "DB_ENV->lock_id_free", DB_INIT_LOCK);
00176 
00177         lt = dbenv->lk_handle;
00178         region = lt->reginfo.primary;
00179 
00180         LOCK_SYSTEM_LOCK(dbenv);
00181         LOCKER_LOCK(lt, region, id, locker_ndx);
00182         if ((ret = __lock_getlocker(lt, id, locker_ndx, 0, &sh_locker)) != 0)
00183                 goto err;
00184 
00185         if (sh_locker == NULL) {
00186                 __db_err(dbenv, "Unknown locker ID: %lx", (u_long)id);
00187                 ret = EINVAL;
00188                 goto err;
00189         }
00190 
00191         if (sh_locker->nlocks != 0) {
00192                 __db_err(dbenv, "Locker still has locks");
00193                 ret = EINVAL;
00194                 goto err;
00195         }
00196 
00197         __lock_freelocker(lt, region, sh_locker, locker_ndx);
00198 
00199 err:    LOCK_SYSTEM_UNLOCK(dbenv);
00200         return (ret);
00201 }
00202 
00203 /*
00204  * __lock_id_set --
00205  *      Set the current locker ID and current maximum unused ID (for
00206  *      testing purposes only).
00207  *
00208  * PUBLIC: int __lock_id_set __P((DB_ENV *, u_int32_t, u_int32_t));
00209  */
00210 int
00211 __lock_id_set(dbenv, cur_id, max_id)
00212         DB_ENV *dbenv;
00213         u_int32_t cur_id, max_id;
00214 {
00215         DB_LOCKTAB *lt;
00216         DB_LOCKREGION *region;
00217 
00218         ENV_REQUIRES_CONFIG(dbenv,
00219             dbenv->lk_handle, "lock_id_set", DB_INIT_LOCK);
00220 
00221         lt = dbenv->lk_handle;
00222         region = lt->reginfo.primary;
00223         region->stat.st_id = cur_id;
00224         region->stat.st_cur_maxid = max_id;
00225 
00226         return (0);
00227 }
00228 
00229 /*
00230  * __lock_getlocker --
00231  *      Get a locker in the locker hash table.  The create parameter
00232  * indicates if the locker should be created if it doesn't exist in
00233  * the table.
00234  *
00235  * This must be called with the locker bucket locked.
00236  *
00237  * PUBLIC: int __lock_getlocker __P((DB_LOCKTAB *,
00238  * PUBLIC:     u_int32_t, u_int32_t, int, DB_LOCKER **));
00239  */
00240 int
00241 __lock_getlocker(lt, locker, indx, create, retp)
00242         DB_LOCKTAB *lt;
00243         u_int32_t locker, indx;
00244         int create;
00245         DB_LOCKER **retp;
00246 {
00247         DB_ENV *dbenv;
00248         DB_LOCKER *sh_locker;
00249         DB_LOCKREGION *region;
00250 
00251         dbenv = lt->dbenv;
00252         region = lt->reginfo.primary;
00253 
00254         HASHLOOKUP(lt->locker_tab,
00255             indx, __db_locker, links, locker, sh_locker, __lock_locker_cmp);
00256 
00257         /*
00258          * If we found the locker, then we can just return it.  If
00259          * we didn't find the locker, then we need to create it.
00260          */
00261         if (sh_locker == NULL && create) {
00262                 /* Create new locker and then insert it into hash table. */
00263                 if ((sh_locker = SH_TAILQ_FIRST(
00264                     &region->free_lockers, __db_locker)) == NULL)
00265                         return (__lock_nomem(dbenv, "locker entries"));
00266                 SH_TAILQ_REMOVE(
00267                     &region->free_lockers, sh_locker, links, __db_locker);
00268                 if (++region->stat.st_nlockers > region->stat.st_maxnlockers)
00269                         region->stat.st_maxnlockers = region->stat.st_nlockers;
00270 
00271                 sh_locker->id = locker;
00272                 dbenv->thread_id(dbenv, &sh_locker->pid, &sh_locker->tid);
00273                 sh_locker->dd_id = 0;
00274                 sh_locker->master_locker = INVALID_ROFF;
00275                 sh_locker->parent_locker = INVALID_ROFF;
00276                 SH_LIST_INIT(&sh_locker->child_locker);
00277                 sh_locker->flags = 0;
00278                 SH_LIST_INIT(&sh_locker->heldby);
00279                 sh_locker->nlocks = 0;
00280                 sh_locker->nwrites = 0;
00281                 sh_locker->lk_timeout = 0;
00282                 LOCK_SET_TIME_INVALID(&sh_locker->tx_expire);
00283                 LOCK_SET_TIME_INVALID(&sh_locker->lk_expire);
00284 
00285                 HASHINSERT(lt->locker_tab, indx, __db_locker, links, sh_locker);
00286                 SH_TAILQ_INSERT_HEAD(&region->lockers,
00287                     sh_locker, ulinks, __db_locker);
00288         }
00289 
00290         *retp = sh_locker;
00291         return (0);
00292 }
00293 
00294 /*
00295  * __lock_addfamilylocker
00296  *      Put a locker entry in for a child transaction.
00297  *
00298  * PUBLIC: int __lock_addfamilylocker __P((DB_ENV *, u_int32_t, u_int32_t));
00299  */
00300 int
00301 __lock_addfamilylocker(dbenv, pid, id)
00302         DB_ENV *dbenv;
00303         u_int32_t pid, id;
00304 {
00305         DB_LOCKER *lockerp, *mlockerp;
00306         DB_LOCKREGION *region;
00307         DB_LOCKTAB *lt;
00308         u_int32_t ndx;
00309         int ret;
00310 
00311         lt = dbenv->lk_handle;
00312         region = lt->reginfo.primary;
00313         LOCK_SYSTEM_LOCK(dbenv);
00314 
00315         /* get/create the  parent locker info */
00316         LOCKER_LOCK(lt, region, pid, ndx);
00317         if ((ret = __lock_getlocker(lt, pid, ndx, 1, &mlockerp)) != 0)
00318                 goto err;
00319 
00320         /*
00321          * We assume that only one thread can manipulate
00322          * a single transaction family.
00323          * Therefore the master locker cannot go away while
00324          * we manipulate it, nor can another child in the
00325          * family be created at the same time.
00326          */
00327         LOCKER_LOCK(lt, region, id, ndx);
00328         if ((ret = __lock_getlocker(lt, id, ndx, 1, &lockerp)) != 0)
00329                 goto err;
00330 
00331         /* Point to our parent. */
00332         lockerp->parent_locker = R_OFFSET(&lt->reginfo, mlockerp);
00333 
00334         /* See if this locker is the family master. */
00335         if (mlockerp->master_locker == INVALID_ROFF)
00336                 lockerp->master_locker = R_OFFSET(&lt->reginfo, mlockerp);
00337         else {
00338                 lockerp->master_locker = mlockerp->master_locker;
00339                 mlockerp = R_ADDR(&lt->reginfo, mlockerp->master_locker);
00340         }
00341 
00342         /*
00343          * Link the child at the head of the master's list.
00344          * The guess is when looking for deadlock that
00345          * the most recent child is the one thats blocked.
00346          */
00347         SH_LIST_INSERT_HEAD(
00348             &mlockerp->child_locker, lockerp, child_link, __db_locker);
00349 
00350 err:    LOCK_SYSTEM_UNLOCK(dbenv);
00351 
00352         return (ret);
00353 }
00354 
00355 /*
00356  * __lock_freefamilylocker
00357  *      Remove a locker from the hash table and its family.
00358  *
00359  * This must be called without the locker bucket locked.
00360  *
00361  * PUBLIC: int __lock_freefamilylocker  __P((DB_LOCKTAB *, u_int32_t));
00362  */
00363 int
00364 __lock_freefamilylocker(lt, locker)
00365         DB_LOCKTAB *lt;
00366         u_int32_t locker;
00367 {
00368         DB_ENV *dbenv;
00369         DB_LOCKER *sh_locker;
00370         DB_LOCKREGION *region;
00371         u_int32_t indx;
00372         int ret;
00373 
00374         dbenv = lt->dbenv;
00375         region = lt->reginfo.primary;
00376 
00377         LOCK_SYSTEM_LOCK(dbenv);
00378         LOCKER_LOCK(lt, region, locker, indx);
00379 
00380         if ((ret = __lock_getlocker(lt,
00381             locker, indx, 0, &sh_locker)) != 0 || sh_locker == NULL)
00382                 goto err;
00383 
00384         if (SH_LIST_FIRST(&sh_locker->heldby, __db_lock) != NULL) {
00385                 ret = EINVAL;
00386                 __db_err(dbenv, "Freeing locker with locks");
00387                 goto err;
00388         }
00389 
00390         /* If this is part of a family, we must fix up its links. */
00391         if (sh_locker->master_locker != INVALID_ROFF)
00392                 SH_LIST_REMOVE(sh_locker, child_link, __db_locker);
00393 
00394         __lock_freelocker(lt, region, sh_locker, indx);
00395 
00396 err:    LOCK_SYSTEM_UNLOCK(dbenv);
00397         return (ret);
00398 }
00399 
00400 /*
00401  * __lock_freelocker
00402  *      Common code for deleting a locker; must be called with the
00403  *      locker bucket locked.
00404  *
00405  * PUBLIC: void __lock_freelocker
00406  * PUBLIC:    __P((DB_LOCKTAB *, DB_LOCKREGION *, DB_LOCKER *, u_int32_t));
00407  */
00408 void
00409 __lock_freelocker(lt, region, sh_locker, indx)
00410         DB_LOCKTAB *lt;
00411         DB_LOCKREGION *region;
00412         DB_LOCKER *sh_locker;
00413         u_int32_t indx;
00414 
00415 {
00416         HASHREMOVE_EL(
00417             lt->locker_tab, indx, __db_locker, links, sh_locker);
00418         SH_TAILQ_INSERT_HEAD(
00419             &region->free_lockers, sh_locker, links, __db_locker);
00420         SH_TAILQ_REMOVE(&region->lockers, sh_locker, ulinks, __db_locker);
00421         region->stat.st_nlockers--;
00422 }

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