00001
00002
00003
00004
00005
00006
00007
00008 #include "config.h"
00009
00010 #ifndef lint
00011 static const char revid[] = "$Id: lock_8c-source.html,v 1.1 2008/06/08 10:19:51 sebdiaz Exp $";
00012 #endif
00013
00014 #ifndef NO_SYSTEM_INCLUDES
00015 #include <sys/types.h>
00016
00017 #include <errno.h>
00018 #include <string.h>
00019 #endif
00020
00021 #ifdef HAVE_RPC
00022 #include "db_server.h"
00023 #endif
00024
00025 #include "db_int.h"
00026 #include "db_page.h"
00027 #include "db_shash.h"
00028 #include "lock.h"
00029 #include "log.h"
00030 #include "db_am.h"
00031 #include "txn.h"
00032
00033 #ifdef HAVE_RPC
00034 #include "gen_client_ext.h"
00035 #include "rpc_client_ext.h"
00036 #endif
00037
00038 static int __lock_checklocker __P((DB_LOCKTAB *,
00039 struct __db_lock *, u_int32_t, u_int32_t, int *));
00040 static int __lock_get_internal __P((DB_LOCKTAB *, u_int32_t,
00041 u_int32_t, const DBT *, db_lockmode_t, DB_LOCK *));
00042 static int __lock_is_parent __P((DB_LOCKTAB *, u_int32_t, DB_LOCKER *));
00043 static int __lock_put_internal __P((DB_LOCKTAB *,
00044 struct __db_lock *, u_int32_t, u_int32_t));
00045 static int __lock_put_nolock __P((DB_ENV *, DB_LOCK *, int *));
00046 static void __lock_remove_waiter __P((DB_LOCKOBJ *,
00047 struct __db_lock *, db_status_t));
00048
00049 static const char __db_lock_err[] = "Lock table is out of available %s";
00050 static const char __db_lock_invalid[] = "%s: Lock is no longer valid";
00051 static const char __db_locker_invalid[] = "Locker is not valid";
00052
00053
00054
00055
00056
00057 int
00058 CDB_lock_id(dbenv, idp)
00059 DB_ENV *dbenv;
00060 u_int32_t *idp;
00061 {
00062 DB_LOCKTAB *lt;
00063 DB_LOCKREGION *region;
00064
00065 #ifdef HAVE_RPC
00066 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
00067 return (__dbcl_lock_id(dbenv, idp));
00068 #endif
00069
00070 PANIC_CHECK(dbenv);
00071 ENV_REQUIRES_CONFIG(dbenv, dbenv->lk_handle, DB_INIT_LOCK);
00072
00073 lt = dbenv->lk_handle;
00074 region = lt->reginfo.primary;
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092 LOCKREGION(dbenv, lt);
00093 if (region->id >= DB_LOCK_MAXID)
00094 region->id = 0;
00095 *idp = ++region->id;
00096 UNLOCKREGION(dbenv, lt);
00097
00098 return (0);
00099 }
00100
00101
00102
00103
00104
00105
00106
00107
00108 int
00109 CDB_lock_vec(dbenv, locker, flags, list, nlist, elistp)
00110 DB_ENV *dbenv;
00111 u_int32_t locker, flags;
00112 int nlist;
00113 DB_LOCKREQ *list, **elistp;
00114 {
00115 struct __db_lock *lp, *next_lock;
00116 DB_LOCKER *sh_locker, *sh_parent;
00117 DB_LOCKOBJ *sh_obj;
00118 DB_LOCKREGION *region;
00119 DB_LOCKTAB *lt;
00120 u_int32_t lndx, ndx;
00121 int did_abort, i, ret, run_dd;
00122
00123 #ifdef HAVE_RPC
00124 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
00125 return (__dbcl_lock_vec(dbenv, locker,
00126 flags, list, nlist, elistp));
00127 #endif
00128 PANIC_CHECK(dbenv);
00129 ENV_REQUIRES_CONFIG(dbenv, dbenv->lk_handle, DB_INIT_LOCK);
00130
00131
00132 if ((ret = CDB___db_fchk(dbenv, "CDB_lock_vec", flags, DB_LOCK_NOWAIT)) != 0)
00133 return (ret);
00134
00135 lt = dbenv->lk_handle;
00136 region = lt->reginfo.primary;
00137
00138 run_dd = 0;
00139 LOCKREGION(dbenv, (DB_LOCKTAB *)dbenv->lk_handle);
00140 for (i = 0, ret = 0; i < nlist && ret == 0; i++)
00141 switch (list[i].op) {
00142 case DB_LOCK_GET:
00143 ret = __lock_get_internal(dbenv->lk_handle,
00144 locker, flags,
00145 list[i].obj, list[i].mode, &list[i].lock);
00146 break;
00147 case DB_LOCK_INHERIT:
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157 ndx = CDB___lock_locker_hash(locker) % region->table_size;
00158 if ((ret = CDB___lock_getlocker(lt,
00159 locker, ndx, 0, &sh_locker)) != 0 ||
00160 sh_locker == NULL ||
00161 F_ISSET(sh_locker, DB_LOCKER_DELETED)) {
00162 if (ret == 0 && sh_locker != NULL)
00163 ret = EACCES;
00164 CDB___db_err(dbenv, __db_locker_invalid);
00165 break;
00166 }
00167
00168
00169 if (sh_locker->parent_locker == INVALID_ROFF) {
00170 CDB___db_err(dbenv, "Not a child transaction");
00171 ret = EINVAL;
00172 break;
00173 }
00174 sh_parent = (DB_LOCKER *)
00175 R_ADDR(<->reginfo, sh_locker->parent_locker);
00176 F_SET(sh_locker, DB_LOCKER_DELETED);
00177
00178
00179
00180
00181
00182 ndx = CDB___lock_locker_hash(locker) % region->table_size;
00183 if (F_ISSET(sh_parent, DB_LOCKER_DELETED)) {
00184 if (ret == 0) {
00185 CDB___db_err(dbenv,
00186 "Parent locker is not valid");
00187 ret = EACCES;
00188 }
00189 break;
00190 }
00191
00192 for (lp = SH_LIST_FIRST(&sh_locker->heldby, __db_lock);
00193 lp != NULL;
00194 lp = SH_LIST_FIRST(&sh_locker->heldby, __db_lock)) {
00195 SH_LIST_REMOVE(lp, locker_links, __db_lock);
00196 SH_LIST_INSERT_HEAD(&sh_parent->heldby, lp,
00197 locker_links, __db_lock);
00198 lp->holder = sh_parent->id;
00199 }
00200
00201
00202 ret = __lock_checklocker(lt,
00203 NULL, locker, DB_LOCK_IGNOREDEL, NULL);
00204 break;
00205 case DB_LOCK_PUT:
00206 ret = __lock_put_nolock(dbenv, &list[i].lock, &run_dd);
00207 break;
00208 case DB_LOCK_PUT_ALL:
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218 ndx = CDB___lock_locker_hash(locker) % region->table_size;
00219 if ((ret = CDB___lock_getlocker(lt,
00220 locker, ndx, 0, &sh_locker)) != 0 ||
00221 sh_locker == NULL ||
00222 F_ISSET(sh_locker, DB_LOCKER_DELETED))
00223
00224
00225
00226
00227
00228 break;
00229 F_SET(sh_locker, DB_LOCKER_DELETED);
00230
00231
00232 for (lp = SH_LIST_FIRST(&sh_locker->heldby, __db_lock);
00233 lp != NULL;
00234 lp = SH_LIST_FIRST(&sh_locker->heldby, __db_lock)) {
00235 SH_LIST_REMOVE(lp, locker_links, __db_lock);
00236 sh_obj =
00237 (DB_LOCKOBJ *)((u_int8_t *)lp + lp->obj);
00238 SHOBJECT_LOCK(lt, region, sh_obj, lndx);
00239 ret = __lock_put_internal(lt,
00240 lp, lndx, DB_LOCK_FREE | DB_LOCK_DOALL);
00241 if (ret != 0)
00242 break;
00243 }
00244 ret = __lock_checklocker(lt,
00245 NULL, locker, DB_LOCK_IGNOREDEL, NULL);
00246 break;
00247 case DB_LOCK_PUT_OBJ:
00248
00249 OBJECT_LOCK(lt, region, list[i].obj, ndx);
00250 if ((ret = CDB___lock_getobj(lt, list[i].obj,
00251 ndx, 0, &sh_obj)) != 0 || sh_obj == NULL) {
00252 if (ret == 0)
00253 ret = EINVAL;
00254 break;
00255 }
00256
00257
00258
00259
00260
00261
00262
00263 for (lp = SH_TAILQ_FIRST(&sh_obj->waiters, __db_lock);
00264 ret == 0 && lp != NULL;
00265 lp = SH_TAILQ_FIRST(&sh_obj->waiters, __db_lock))
00266 ret = __lock_put_internal(lt,
00267 lp, ndx, DB_LOCK_NOPROMOTE | DB_LOCK_DOALL);
00268
00269
00270
00271
00272
00273
00274 for (lp = SH_TAILQ_FIRST(&sh_obj->holders, __db_lock);
00275 ret == 0 && lp != NULL;
00276 lp = next_lock) {
00277 next_lock = SH_TAILQ_NEXT(lp, links, __db_lock);
00278 ret = __lock_put_internal(lt,
00279 lp, ndx, DB_LOCK_NOPROMOTE | DB_LOCK_DOALL);
00280 }
00281 break;
00282 #ifdef DEBUG
00283 case DB_LOCK_DUMP:
00284
00285 ndx = CDB___lock_locker_hash(locker) % region->table_size;
00286 if ((ret = CDB___lock_getlocker(lt,
00287 locker, ndx, 0, &sh_locker)) != 0
00288 || sh_locker == NULL
00289 || F_ISSET(sh_locker, DB_LOCKER_DELETED))
00290 break;
00291
00292 for (lp = SH_LIST_FIRST(&sh_locker->heldby, __db_lock);
00293 lp != NULL;
00294 lp = SH_LIST_NEXT(lp, locker_links, __db_lock)) {
00295 CDB___lock_printlock(lt, lp, 1);
00296 }
00297 break;
00298 #endif
00299 default:
00300 CDB___db_err(dbenv,
00301 "Invalid lock operation: %d", list[i].op);
00302 ret = EINVAL;
00303 break;
00304 }
00305
00306 if (ret == 0 && region->need_dd && region->detect != DB_LOCK_NORUN) {
00307 run_dd = 1;
00308 region->need_dd = 0;
00309 }
00310 UNLOCKREGION(dbenv, (DB_LOCKTAB *)dbenv->lk_handle);
00311
00312 if (run_dd)
00313 (void)CDB_lock_detect(dbenv, 0, region->detect, &did_abort);
00314
00315 if (ret != 0 && elistp != NULL)
00316 *elistp = &list[i - 1];
00317
00318 return (ret);
00319 }
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330 int
00331 CDB_lock_get(dbenv, locker, flags, obj, lock_mode, lock)
00332 DB_ENV *dbenv;
00333 u_int32_t locker, flags;
00334 const DBT *obj;
00335 db_lockmode_t lock_mode;
00336 DB_LOCK *lock;
00337 {
00338 int ret;
00339
00340 #ifdef HAVE_RPC
00341 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
00342 return (__dbcl_lock_get(dbenv, locker,
00343 flags, obj, lock_mode, lock));
00344 #endif
00345 PANIC_CHECK(dbenv);
00346 ENV_REQUIRES_CONFIG(dbenv, dbenv->lk_handle, DB_INIT_LOCK);
00347
00348 if (IS_RECOVERING(dbenv))
00349 return (0);
00350
00351
00352 if ((ret = CDB___db_fchk(dbenv,
00353 "CDB_lock_get", flags, DB_LOCK_NOWAIT | DB_LOCK_UPGRADE)) != 0)
00354 return (ret);
00355
00356 if (lock == NULL)
00357 return (EINVAL);
00358
00359 LOCKREGION(dbenv, (DB_LOCKTAB *)dbenv->lk_handle);
00360 ret = __lock_get_internal(dbenv->lk_handle,
00361 locker, flags, obj, lock_mode, lock);
00362 UNLOCKREGION(dbenv, (DB_LOCKTAB *)dbenv->lk_handle);
00363 return (ret);
00364 }
00365
00366 static int
00367 __lock_get_internal(lt, locker, flags, obj, lock_mode, lock)
00368 DB_LOCKTAB *lt;
00369 u_int32_t locker, flags;
00370 const DBT *obj;
00371 db_lockmode_t lock_mode;
00372 DB_LOCK *lock;
00373 {
00374 struct __db_lock *newl, *lp;
00375 DB_ENV *dbenv;
00376 DB_LOCKER *sh_locker;
00377 DB_LOCKOBJ *sh_obj;
00378 DB_LOCKREGION *region;
00379 u_int32_t locker_ndx;
00380 int did_abort, freed, ihold, on_locker_list, no_dd, ret;
00381
00382 no_dd = ret = 0;
00383 on_locker_list = 0;
00384 region = lt->reginfo.primary;
00385 dbenv = lt->dbenv;
00386
00387
00388
00389
00390 if ((u_int32_t)lock_mode >= region->nmodes) {
00391 CDB___db_err(dbenv,
00392 "CDB_lock_get: invalid lock mode %lu\n", (u_long)lock_mode);
00393 return (EINVAL);
00394 }
00395
00396
00397 region->nrequests++;
00398 if ((newl = SH_TAILQ_FIRST(®ion->free_locks, __db_lock)) != NULL)
00399 SH_TAILQ_REMOVE(®ion->free_locks, newl, links, __db_lock);
00400 if (newl == NULL) {
00401 CDB___db_err(dbenv, __db_lock_err, "locks");
00402 return (ENOMEM);
00403 }
00404
00405
00406 OBJECT_LOCK(lt, region, obj, lock->ndx);
00407 if ((ret = CDB___lock_getobj(lt, obj, lock->ndx, 1, &sh_obj)) != 0)
00408 goto err;
00409
00410
00411 locker_ndx = CDB___lock_locker_hash(locker) % region->table_size;
00412 if ((ret =
00413 CDB___lock_getlocker(lt, locker, locker_ndx, 1, &sh_locker)) != 0) {
00414
00415
00416
00417
00418 return (ret);
00419 }
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442 ihold = 0;
00443 for (lp = SH_TAILQ_FIRST(&sh_obj->holders, __db_lock);
00444 lp != NULL;
00445 lp = SH_TAILQ_NEXT(lp, links, __db_lock)) {
00446 if (locker == lp->holder ||
00447 __lock_is_parent(lt, lp->holder, sh_locker)) {
00448 if (lp->mode == lock_mode &&
00449 lp->status == DB_LSTAT_HELD) {
00450 if (LF_ISSET(DB_LOCK_UPGRADE))
00451 goto upgrade;
00452
00453
00454
00455
00456
00457 lp->refcount++;
00458 lock->off = R_OFFSET(<->reginfo, lp);
00459 lock->gen = lp->gen;
00460
00461 ret = 0;
00462 goto done;
00463 } else
00464 ihold = 1;
00465 } else if (CONFLICTS(lt, region, lp->mode, lock_mode))
00466 break;
00467 }
00468
00469
00470
00471
00472
00473
00474
00475 newl->holder = locker;
00476 newl->refcount = 1;
00477 newl->mode = lock_mode;
00478 newl->obj = SH_PTR_TO_OFF(newl, sh_obj);
00479 newl->status = DB_LSTAT_HELD;
00480
00481
00482
00483
00484
00485
00486
00487 if (LF_ISSET(DB_LOCK_UPGRADE)) {
00488 if (lp == NULL)
00489 goto upgrade;
00490
00491
00492
00493
00494
00495 if (SH_TAILQ_FIRST(&sh_obj->waiters, __db_lock) == NULL)
00496 SH_TAILQ_INSERT_HEAD(®ion->dd_objs,
00497 sh_obj, dd_links, __db_lockobj);
00498
00499 SH_TAILQ_INSERT_HEAD(&sh_obj->waiters, newl, links, __db_lock);
00500 goto llist;
00501 }
00502
00503 if (lp == NULL && !ihold)
00504 for (lp = SH_TAILQ_FIRST(&sh_obj->waiters, __db_lock);
00505 lp != NULL;
00506 lp = SH_TAILQ_NEXT(lp, links, __db_lock)) {
00507 if (CONFLICTS(lt, region, lp->mode, lock_mode) &&
00508 locker != lp->holder)
00509 break;
00510 }
00511 if (lp == NULL)
00512 SH_TAILQ_INSERT_TAIL(&sh_obj->holders, newl, links);
00513 else if (!(flags & DB_LOCK_NOWAIT)) {
00514
00515
00516
00517
00518 if (SH_TAILQ_FIRST(&sh_obj->waiters, __db_lock) == NULL)
00519 SH_TAILQ_INSERT_HEAD(®ion->dd_objs,
00520 sh_obj, dd_links, __db_lockobj);
00521 SH_TAILQ_INSERT_TAIL(&sh_obj->waiters, newl, links);
00522 } else {
00523 ret = DB_LOCK_NOTGRANTED;
00524 if (SH_LIST_FIRST(&sh_locker->heldby, __db_lock) == NULL
00525 && LOCKER_FREEABLE(sh_locker))
00526 CDB___lock_freelocker( lt, region, sh_locker, locker_ndx);
00527 region->nnowaits++;
00528 goto err;
00529 }
00530
00531 llist:
00532
00533
00534
00535
00536
00537 on_locker_list = 1;
00538 no_dd = sh_locker->master_locker == INVALID_ROFF
00539 && SH_LIST_FIRST(&sh_locker->child_locker, __db_locker) == NULL
00540 && SH_LIST_FIRST(&sh_locker->heldby, __db_lock) == NULL;
00541
00542 SH_LIST_INSERT_HEAD(&sh_locker->heldby, newl, locker_links, __db_lock);
00543
00544 if (lp != NULL) {
00545
00546
00547
00548
00549
00550 newl->status = DB_LSTAT_WAITING;
00551 region->nconflicts++;
00552 if (region->detect == DB_LOCK_NORUN)
00553 region->need_dd = 1;
00554 UNLOCKREGION(dbenv, (DB_LOCKTAB *)dbenv->lk_handle);
00555
00556
00557
00558
00559
00560 if (region->detect != DB_LOCK_NORUN && !no_dd)
00561 (void)CDB_lock_detect(dbenv, 0, region->detect, &did_abort);
00562
00563 MUTEX_LOCK(&newl->mutex, dbenv->lockfhp);
00564 LOCKREGION(dbenv, (DB_LOCKTAB *)dbenv->lk_handle);
00565
00566 if (newl->status != DB_LSTAT_PENDING) {
00567 (void)__lock_checklocker(lt,
00568 newl, newl->holder, 0, &freed);
00569 switch (newl->status) {
00570 case DB_LSTAT_ABORTED:
00571 on_locker_list = 0;
00572 ret = DB_LOCK_DEADLOCK;
00573 break;
00574 case DB_LSTAT_NOGRANT:
00575 ret = DB_LOCK_NOTGRANTED;
00576 break;
00577 default:
00578 ret = EINVAL;
00579 break;
00580 }
00581 goto err;
00582 } else if (LF_ISSET(DB_LOCK_UPGRADE)) {
00583
00584
00585
00586
00587
00588 SH_TAILQ_REMOVE(
00589 &sh_obj->holders, newl, links, __db_lock);
00590
00591
00592
00593
00594 newl->links.stqe_prev = -1;
00595 goto upgrade;
00596 } else
00597 newl->status = DB_LSTAT_HELD;
00598 }
00599
00600 lock->off = R_OFFSET(<->reginfo, newl);
00601 lock->gen = newl->gen;
00602
00603 return (0);
00604
00605 upgrade:
00606
00607
00608
00609 ((struct __db_lock *)R_ADDR(<->reginfo, lock->off))->mode = lock_mode;
00610
00611 ret = 0;
00612
00613
00614 done:
00615 err: newl->status = DB_LSTAT_FREE;
00616 if (on_locker_list) {
00617 SH_LIST_REMOVE(newl, locker_links, __db_lock);
00618 }
00619 SH_TAILQ_INSERT_HEAD(®ion->free_locks, newl, links, __db_lock);
00620 return (ret);
00621 }
00622
00623
00624
00625
00626
00627
00628
00629 int
00630 CDB_lock_put(dbenv, lock)
00631 DB_ENV *dbenv;
00632 DB_LOCK *lock;
00633 {
00634 DB_LOCKTAB *lt;
00635 int ret, run_dd;
00636
00637 #ifdef HAVE_RPC
00638 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
00639 return (__dbcl_lock_put(dbenv, lock));
00640 #endif
00641 PANIC_CHECK(dbenv);
00642 ENV_REQUIRES_CONFIG(dbenv, dbenv->lk_handle, DB_INIT_LOCK);
00643
00644 if (IS_RECOVERING(dbenv))
00645 return (0);
00646
00647 lt = dbenv->lk_handle;
00648
00649 LOCKREGION(dbenv, lt);
00650 ret = __lock_put_nolock(dbenv, lock, &run_dd);
00651 UNLOCKREGION(dbenv, lt);
00652
00653 lock->off = LOCK_INVALID;
00654
00655 if (ret == 0 && run_dd)
00656 (void)CDB_lock_detect(dbenv, 0,
00657 ((DB_LOCKREGION *)lt->reginfo.primary)->detect, NULL);
00658 return (ret);
00659 }
00660
00661 static int
00662 __lock_put_nolock(dbenv, lock, runp)
00663 DB_ENV *dbenv;
00664 DB_LOCK *lock;
00665 int *runp;
00666 {
00667 struct __db_lock *lockp;
00668 DB_LOCKREGION *region;
00669 DB_LOCKTAB *lt;
00670 u_int32_t locker;
00671 int ret;
00672
00673 lt = dbenv->lk_handle;
00674 region = lt->reginfo.primary;
00675
00676 lockp = (struct __db_lock *)R_ADDR(<->reginfo, lock->off);
00677 if (lock->gen != lockp->gen) {
00678 CDB___db_err(dbenv, __db_lock_invalid, "CDB_lock_put");
00679 return(EACCES);
00680 }
00681
00682 locker = lockp->holder;
00683 ret = __lock_put_internal(lt,
00684 lockp, lock->ndx, DB_LOCK_UNLINK | DB_LOCK_FREE);
00685
00686 *runp = 0;
00687 if (ret == 0 && region->need_dd && region->detect != DB_LOCK_NORUN) {
00688 *runp = 1;
00689 region->need_dd = 0;
00690 }
00691
00692 return (ret);
00693 }
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703 int
00704 CDB___lock_downgrade(dbenv, lock, new_mode, flags)
00705 DB_ENV *dbenv;
00706 DB_LOCK *lock;
00707 db_lockmode_t new_mode;
00708 u_int32_t flags;
00709 {
00710 struct __db_lock *lockp;
00711 DB_LOCKOBJ *obj;
00712 DB_LOCKREGION *region;
00713 DB_LOCKTAB *lt;
00714 int ret;
00715
00716 COMPQUIET(flags, 0);
00717
00718 PANIC_CHECK(dbenv);
00719
00720 lt = dbenv->lk_handle;
00721 region = lt->reginfo.primary;
00722
00723 LOCKREGION(dbenv, lt);
00724
00725 lockp = (struct __db_lock *)R_ADDR(<->reginfo, lock->off);
00726 if (lock->gen != lockp->gen) {
00727 CDB___db_err(dbenv, __db_lock_invalid, "lock_downgrade");
00728 ret = EACCES;
00729 goto out;
00730 }
00731
00732 lockp->mode = new_mode;
00733
00734
00735 obj = (DB_LOCKOBJ *)((u_int8_t *)lockp + lockp->obj);
00736 (void)CDB___lock_promote(lt, obj);
00737
00738 ++region->nreleases;
00739 out: UNLOCKREGION(dbenv, lt);
00740
00741 return (0);
00742 }
00743
00744 static int
00745 __lock_put_internal(lt, lockp, obj_ndx, flags)
00746 DB_LOCKTAB *lt;
00747 struct __db_lock *lockp;
00748 u_int32_t obj_ndx;
00749 u_int32_t flags;
00750 {
00751 DB_LOCKOBJ *sh_obj;
00752 DB_LOCKREGION *region;
00753 int no_reclaim, ret, state_changed;
00754
00755 region = lt->reginfo.primary;
00756 no_reclaim = ret = state_changed = 0;
00757
00758 if (!OBJ_LINKS_VALID(lockp)) {
00759
00760
00761
00762
00763
00764
00765 lockp->status = DB_LSTAT_FREE;
00766 SH_TAILQ_INSERT_HEAD(
00767 ®ion->free_locks, lockp, links, __db_lock);
00768 return (0);
00769 }
00770
00771 if (LF_ISSET(DB_LOCK_DOALL))
00772 region->nreleases += lockp->refcount;
00773 else
00774 region->nreleases++;
00775
00776 if (!LF_ISSET(DB_LOCK_DOALL) && lockp->refcount > 1) {
00777 lockp->refcount--;
00778 return (0);
00779 }
00780
00781
00782 lockp->gen++;
00783
00784
00785 sh_obj = (DB_LOCKOBJ *)((u_int8_t *)lockp + lockp->obj);
00786
00787
00788 if (lockp->status != DB_LSTAT_HELD)
00789 __lock_remove_waiter(sh_obj, lockp, DB_LSTAT_FREE);
00790 else {
00791 SH_TAILQ_REMOVE(&sh_obj->holders, lockp, links, __db_lock);
00792 lockp->links.stqe_prev = -1;
00793 }
00794
00795 if (LF_ISSET(DB_LOCK_NOPROMOTE))
00796 state_changed = 0;
00797 else
00798 state_changed = CDB___lock_promote(lt, sh_obj);
00799
00800 if (LF_ISSET(DB_LOCK_UNLINK))
00801 ret = __lock_checklocker(lt, lockp, lockp->holder, flags, NULL);
00802
00803
00804 if (SH_TAILQ_FIRST(&sh_obj->holders, __db_lock) == NULL) {
00805 HASHREMOVE_EL(lt->obj_tab,
00806 obj_ndx, __db_lockobj, links, sh_obj);
00807 if (sh_obj->lockobj.size > sizeof(sh_obj->objdata))
00808 CDB___db_shalloc_free(lt->reginfo.addr,
00809 SH_DBT_PTR(&sh_obj->lockobj));
00810 SH_TAILQ_INSERT_HEAD(
00811 ®ion->free_objs, sh_obj, links, __db_lockobj);
00812 state_changed = 1;
00813 }
00814
00815
00816 if (!LF_ISSET(DB_LOCK_UNLINK) && LF_ISSET(DB_LOCK_FREE)) {
00817 lockp->status = DB_LSTAT_FREE;
00818 SH_TAILQ_INSERT_HEAD(
00819 ®ion->free_locks, lockp, links, __db_lock);
00820 }
00821
00822
00823
00824
00825
00826 if (state_changed == 0)
00827 region->need_dd = 1;
00828
00829 return (ret);
00830 }
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843 static int
00844 __lock_checklocker(lt, lockp, locker, flags, freed)
00845 DB_LOCKTAB *lt;
00846 struct __db_lock *lockp;
00847 u_int32_t locker, flags;
00848 int *freed;
00849 {
00850 DB_ENV *dbenv;
00851 DB_LOCKER *sh_locker;
00852 DB_LOCKREGION *region;
00853 u_int32_t indx;
00854 int ret;
00855
00856 dbenv = lt->dbenv;
00857 region = lt->reginfo.primary;
00858 ret = 0;
00859
00860 if (freed != NULL)
00861 *freed = 0;
00862
00863 indx = CDB___lock_locker_hash(locker) % region->table_size;
00864
00865
00866 if ((ret = CDB___lock_getlocker(lt,
00867 locker, indx, 0, &sh_locker)) != 0 || sh_locker == NULL) {
00868 if (ret == 0)
00869 ret = EACCES;
00870 CDB___db_err(lt->dbenv, __db_locker_invalid);
00871 goto freelock;
00872 }
00873
00874 if (F_ISSET(sh_locker, DB_LOCKER_DELETED)) {
00875 LF_CLR(DB_LOCK_FREE);
00876 if (!LF_ISSET(DB_LOCK_IGNOREDEL))
00877 goto freelock;
00878 }
00879
00880 if (LF_ISSET(DB_LOCK_UNLINK))
00881 SH_LIST_REMOVE(lockp, locker_links, __db_lock);
00882
00883 if (SH_LIST_FIRST(&sh_locker->heldby, __db_lock) == NULL
00884 && LOCKER_FREEABLE(sh_locker)) {
00885 CDB___lock_freelocker( lt, region, sh_locker, indx);
00886 if (freed != NULL)
00887 *freed = 1;
00888 }
00889
00890 freelock:
00891 if (LF_ISSET(DB_LOCK_FREE)) {
00892 lockp->status = DB_LSTAT_FREE;
00893 SH_TAILQ_INSERT_HEAD(
00894 ®ion->free_locks, lockp, links, __db_lock);
00895 }
00896
00897 return (ret);
00898 }
00899
00900
00901
00902
00903
00904
00905
00906 int
00907 CDB___lock_addfamilylocker(dbenv, pid, id)
00908 DB_ENV *dbenv;
00909 u_int32_t pid, id;
00910 {
00911 DB_LOCKER *lockerp, *mlockerp;
00912 DB_LOCKREGION *region;
00913 DB_LOCKTAB *lt;
00914 u_int32_t ndx;
00915 int ret;
00916
00917 lt = dbenv->lk_handle;
00918 region = lt->reginfo.primary;
00919 LOCKREGION(dbenv, lt);
00920
00921
00922 ndx = CDB___lock_locker_hash(pid) % region->table_size;
00923 if ((ret = CDB___lock_getlocker(dbenv->lk_handle,
00924 pid, ndx, 1, &mlockerp)) != 0)
00925 goto err;
00926
00927
00928
00929
00930
00931
00932
00933
00934 ndx = CDB___lock_locker_hash(id) % region->table_size;
00935 if ((ret = CDB___lock_getlocker(dbenv->lk_handle,
00936 id, ndx, 1, &lockerp)) != 0)
00937 goto err;
00938
00939
00940 lockerp->parent_locker = R_OFFSET(<->reginfo, mlockerp);
00941
00942
00943 if (mlockerp->master_locker == INVALID_ROFF)
00944 lockerp->master_locker = R_OFFSET(<->reginfo, mlockerp);
00945 else {
00946 lockerp->master_locker = mlockerp->master_locker;
00947 mlockerp = R_ADDR(<->reginfo, mlockerp->master_locker);
00948 }
00949
00950
00951
00952
00953
00954
00955 SH_LIST_INSERT_HEAD(
00956 &mlockerp->child_locker, lockerp, child_link, __db_locker);
00957
00958 err:
00959 UNLOCKREGION(dbenv, lt);
00960
00961 return (ret);
00962 }
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972 int
00973 CDB___lock_freefamilylocker(lt, locker)
00974 DB_LOCKTAB *lt;
00975 u_int32_t locker;
00976 {
00977 DB_ENV *dbenv;
00978 DB_LOCKER *sh_locker;
00979 DB_LOCKREGION *region;
00980 u_int32_t indx;
00981 int ret;
00982
00983 dbenv = lt->dbenv;
00984 region = lt->reginfo.primary;
00985
00986 LOCKREGION(dbenv, lt);
00987 indx = CDB___lock_locker_hash(locker) % region->table_size;
00988
00989 if ((ret = CDB___lock_getlocker(lt,
00990 locker, indx, 0, &sh_locker)) != 0 || sh_locker == NULL) {
00991 if (ret == 0)
00992 ret = EACCES;
00993 goto freelock;
00994 }
00995 if (SH_LIST_FIRST(&sh_locker->heldby, __db_lock) != NULL) {
00996 ret = EINVAL;
00997 CDB___db_err(dbenv, "Freeing locker with locks");
00998 goto freelock;
00999 }
01000
01001
01002 if (sh_locker->master_locker != INVALID_ROFF)
01003 SH_LIST_REMOVE(sh_locker, child_link, __db_locker);
01004
01005 CDB___lock_freelocker(lt, region, sh_locker, indx);
01006
01007 freelock:
01008 UNLOCKREGION(dbenv, lt);
01009 return (ret);
01010 }
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021 void
01022 CDB___lock_freelocker(lt, region, sh_locker, indx)
01023 DB_LOCKTAB *lt;
01024 DB_LOCKREGION *region;
01025 DB_LOCKER *sh_locker;
01026 u_int32_t indx;
01027
01028 {
01029 HASHREMOVE_EL(
01030 lt->locker_tab, indx, __db_locker, links, sh_locker);
01031 SH_TAILQ_INSERT_HEAD(
01032 ®ion->free_lockers, sh_locker, links, __db_locker);
01033 region->nlockers--;
01034 }
01035
01036
01037
01038
01039
01040
01041
01042
01043
01044
01045
01046 int
01047 CDB___lock_getlocker(lt, locker, indx, create, retp)
01048 DB_LOCKTAB *lt;
01049 u_int32_t locker, indx;
01050 int create;
01051 DB_LOCKER **retp;
01052 {
01053 DB_ENV *dbenv;
01054 DB_LOCKER *sh_locker;
01055 DB_LOCKREGION *region;
01056
01057 dbenv = lt->dbenv;
01058 region = lt->reginfo.primary;
01059
01060 HASHLOOKUP(lt->locker_tab,
01061 indx, __db_locker, links, locker, sh_locker, CDB___lock_locker_cmp);
01062
01063
01064
01065
01066
01067 if (sh_locker == NULL && create) {
01068
01069 if ((sh_locker = SH_TAILQ_FIRST(
01070 ®ion->free_lockers, __db_locker)) == NULL) {
01071 CDB___db_err(lt->dbenv, __db_lock_err, "locker entries");
01072 return (ENOMEM);
01073 }
01074 SH_TAILQ_REMOVE(
01075 ®ion->free_lockers, sh_locker, links, __db_locker);
01076 if (++region->nlockers > region->maxnlockers)
01077 region->maxnlockers = region->nlockers;
01078
01079 sh_locker->id = locker;
01080 sh_locker->dd_id = 0;
01081 sh_locker->master_locker = INVALID_ROFF;
01082 sh_locker->parent_locker = INVALID_ROFF;
01083 SH_LIST_INIT(&sh_locker->child_locker);
01084 sh_locker->flags = 0;
01085 SH_LIST_INIT(&sh_locker->heldby);
01086
01087 HASHINSERT(lt->locker_tab, indx, __db_locker, links, sh_locker);
01088 }
01089
01090 *retp = sh_locker;
01091 return (0);
01092 }
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105 int
01106 CDB___lock_getobj(lt, obj, ndx, create, retp)
01107 DB_LOCKTAB *lt;
01108 const DBT *obj;
01109 u_int32_t ndx;
01110 int create;
01111 DB_LOCKOBJ **retp;
01112 {
01113 DB_ENV *dbenv;
01114 DB_LOCKOBJ *sh_obj;
01115 DB_LOCKREGION *region;
01116 int ret;
01117 void *p;
01118
01119 dbenv = lt->dbenv;
01120 region = lt->reginfo.primary;
01121
01122
01123 HASHLOOKUP(lt->obj_tab,
01124 ndx, __db_lockobj, links, obj, sh_obj, CDB___lock_cmp);
01125
01126
01127
01128
01129
01130 if (sh_obj == NULL && create) {
01131
01132 if ((sh_obj =
01133 SH_TAILQ_FIRST(®ion->free_objs, __db_lockobj)) == NULL) {
01134 CDB___db_err(lt->dbenv, __db_lock_err, "object entries");
01135 ret = ENOMEM;
01136 goto err;
01137 }
01138
01139
01140
01141
01142
01143 if (obj->size <= sizeof(sh_obj->objdata))
01144 p = sh_obj->objdata;
01145 else if ((ret = CDB___db_shalloc(
01146 lt->reginfo.addr, obj->size, 0, &p)) != 0) {
01147 CDB___db_err(dbenv, "No space for lock object storage");
01148 goto err;
01149 }
01150
01151 memcpy(p, obj->data, obj->size);
01152
01153 SH_TAILQ_REMOVE(
01154 ®ion->free_objs, sh_obj, links, __db_lockobj);
01155
01156 SH_TAILQ_INIT(&sh_obj->waiters);
01157 SH_TAILQ_INIT(&sh_obj->holders);
01158 sh_obj->lockobj.size = obj->size;
01159 sh_obj->lockobj.off = SH_PTR_TO_OFF(&sh_obj->lockobj, p);
01160
01161 HASHINSERT(lt->obj_tab, ndx, __db_lockobj, links, sh_obj);
01162 }
01163
01164 *retp = sh_obj;
01165 return (0);
01166
01167 err: return (ret);
01168 }
01169
01170
01171
01172
01173
01174
01175
01176
01177 static int
01178 __lock_is_parent(lt, locker, sh_locker)
01179 DB_LOCKTAB *lt;
01180 u_int32_t locker;
01181 DB_LOCKER *sh_locker;
01182 {
01183 DB_LOCKER *parent;
01184
01185 parent = sh_locker;
01186 while (parent->parent_locker != INVALID_ROFF) {
01187 parent = (DB_LOCKER *)
01188 R_ADDR(<->reginfo, parent->parent_locker);
01189 if (parent->id == locker)
01190 return (1);
01191 }
01192
01193 return (0);
01194 }
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204 int
01205 CDB___lock_promote(lt, obj)
01206 DB_LOCKTAB *lt;
01207 DB_LOCKOBJ *obj;
01208 {
01209 struct __db_lock *lp_w, *lp_h, *next_waiter;
01210 DB_LOCKER *sh_locker;
01211 DB_LOCKREGION *region;
01212 u_int32_t locker_ndx;
01213 int had_waiters, state_changed;
01214
01215 region = lt->reginfo.primary;
01216 had_waiters = 0;
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231 for (lp_w = SH_TAILQ_FIRST(&obj->waiters, __db_lock),
01232 state_changed = lp_w == NULL;
01233 lp_w != NULL;
01234 lp_w = next_waiter) {
01235 had_waiters = 1;
01236 next_waiter = SH_TAILQ_NEXT(lp_w, links, __db_lock);
01237 for (lp_h = SH_TAILQ_FIRST(&obj->holders, __db_lock);
01238 lp_h != NULL;
01239 lp_h = SH_TAILQ_NEXT(lp_h, links, __db_lock)) {
01240 if (lp_h->holder != lp_w->holder &&
01241 CONFLICTS(lt, region, lp_h->mode, lp_w->mode)) {
01242
01243 locker_ndx = CDB___lock_locker_hash(lp_w->holder)
01244 % region->table_size;
01245 if ((CDB___lock_getlocker(lt, lp_w->holder,
01246 locker_ndx, 0, &sh_locker)) != 0) {
01247 DB_ASSERT(0);
01248 break;
01249 }
01250 if (!__lock_is_parent(lt, lp_h->holder, sh_locker))
01251 break;
01252 }
01253 }
01254 if (lp_h != NULL)
01255 break;
01256
01257
01258 SH_TAILQ_REMOVE(&obj->waiters, lp_w, links, __db_lock);
01259 lp_w->status = DB_LSTAT_PENDING;
01260 SH_TAILQ_INSERT_TAIL(&obj->holders, lp_w, links);
01261
01262
01263 MUTEX_UNLOCK(&lp_w->mutex);
01264 state_changed = 1;
01265 }
01266
01267
01268
01269
01270
01271 if (had_waiters && SH_TAILQ_FIRST(&obj->waiters, __db_lock) == NULL)
01272 SH_TAILQ_REMOVE(®ion->dd_objs, obj, dd_links, __db_lockobj);
01273 return (state_changed);
01274 }
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286 static void
01287 __lock_remove_waiter(sh_obj, lockp, status)
01288 DB_LOCKOBJ *sh_obj;
01289 struct __db_lock *lockp;
01290 db_status_t status;
01291 {
01292 int do_wakeup;
01293
01294 do_wakeup = lockp->status == DB_LSTAT_WAITING;
01295
01296 SH_TAILQ_REMOVE(&sh_obj->waiters, lockp, links, __db_lock);
01297 lockp->links.stqe_prev = -1;
01298 lockp->status = status;
01299
01300
01301
01302
01303
01304
01305
01306 if (do_wakeup)
01307 MUTEX_UNLOCK(&lockp->mutex);
01308 }
01309
01310
01311
01312
01313
01314
01315 void
01316 CDB___lock_printlock(lt, lp, ispgno)
01317 DB_LOCKTAB *lt;
01318 struct __db_lock *lp;
01319 int ispgno;
01320 {
01321 DB_LOCKOBJ *lockobj;
01322 db_pgno_t pgno;
01323 u_int32_t *fidp;
01324 u_int8_t *ptr, type;
01325 const char *mode, *status;
01326
01327 switch (lp->mode) {
01328 case DB_LOCK_IREAD:
01329 mode = "IREAD";
01330 break;
01331 case DB_LOCK_IWR:
01332 mode = "IWR";
01333 break;
01334 case DB_LOCK_IWRITE:
01335 mode = "IWRITE";
01336 break;
01337 case DB_LOCK_NG:
01338 mode = "NG";
01339 break;
01340 case DB_LOCK_READ:
01341 mode = "READ";
01342 break;
01343 case DB_LOCK_WRITE:
01344 mode = "WRITE";
01345 break;
01346 default:
01347 mode = "UNKNOWN";
01348 break;
01349 }
01350 switch (lp->status) {
01351 case DB_LSTAT_ABORTED:
01352 status = "ABORT";
01353 break;
01354 case DB_LSTAT_ERR:
01355 status = "ERROR";
01356 break;
01357 case DB_LSTAT_FREE:
01358 status = "FREE";
01359 break;
01360 case DB_LSTAT_HELD:
01361 status = "HELD";
01362 break;
01363 case DB_LSTAT_NOGRANT:
01364 status = "NONE";
01365 break;
01366 case DB_LSTAT_WAITING:
01367 status = "WAIT";
01368 break;
01369 case DB_LSTAT_PENDING:
01370 status = "PENDING";
01371 break;
01372 default:
01373 status = "UNKNOWN";
01374 break;
01375 }
01376 printf("\t%lx\t%s\t%lu\t%s\t",
01377 (u_long)lp->holder, mode, (u_long)lp->refcount, status);
01378
01379 lockobj = (DB_LOCKOBJ *)((u_int8_t *)lp + lp->obj);
01380 ptr = SH_DBT_PTR(&lockobj->lockobj);
01381 if (ispgno && lockobj->lockobj.size == sizeof(struct __db_ilock)) {
01382
01383 memcpy(&pgno, ptr, sizeof(db_pgno_t));
01384 fidp = (u_int32_t *)(ptr + sizeof(db_pgno_t));
01385 type = *(u_int8_t *)(ptr + sizeof(db_pgno_t) + DB_FILE_ID_LEN);
01386 printf("%s %lu (%lu %lu %lu %lu %lu)\n",
01387 type == DB_PAGE_LOCK ? "page" : "record",
01388 (u_long)pgno,
01389 (u_long)fidp[0], (u_long)fidp[1], (u_long)fidp[2],
01390 (u_long)fidp[3], (u_long)fidp[4]);
01391 } else {
01392 printf("0x%lx ", (u_long)R_OFFSET(<->reginfo, lockobj));
01393 CDB___db_pr(ptr, lockobj->lockobj.size);
01394 printf("\n");
01395 }
01396 }