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

txn_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: txn_region.c,v 12.10 2005/10/14 21:12:18 ubell Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 
00015 #if TIME_WITH_SYS_TIME
00016 #include <sys/time.h>
00017 #include <time.h>
00018 #else
00019 #if HAVE_SYS_TIME_H
00020 #include <sys/time.h>
00021 #else
00022 #include <time.h>
00023 #endif
00024 #endif
00025 
00026 #include <string.h>
00027 #endif
00028 
00029 #include "db_int.h"
00030 #include "dbinc/log.h"
00031 #include "dbinc/txn.h"
00032 
00033 static int __txn_init __P((DB_ENV *, DB_TXNMGR *));
00034 static size_t __txn_region_size __P((DB_ENV *));
00035 
00036 /*
00037  * __txn_open --
00038  *      Open a transaction region.
00039  *
00040  * PUBLIC: int __txn_open __P((DB_ENV *));
00041  */
00042 int
00043 __txn_open(dbenv)
00044         DB_ENV *dbenv;
00045 {
00046         DB_TXNMGR *mgr;
00047         int ret;
00048 
00049         /* Create/initialize the transaction manager structure. */
00050         if ((ret = __os_calloc(dbenv, 1, sizeof(DB_TXNMGR), &mgr)) != 0)
00051                 return (ret);
00052         TAILQ_INIT(&mgr->txn_chain);
00053         mgr->dbenv = dbenv;
00054 
00055         /* Join/create the txn region. */
00056         mgr->reginfo.dbenv = dbenv;
00057         mgr->reginfo.type = REGION_TYPE_TXN;
00058         mgr->reginfo.id = INVALID_REGION_ID;
00059         mgr->reginfo.flags = REGION_JOIN_OK;
00060         if (F_ISSET(dbenv, DB_ENV_CREATE))
00061                 F_SET(&mgr->reginfo, REGION_CREATE_OK);
00062         if ((ret = __db_r_attach(dbenv,
00063             &mgr->reginfo, __txn_region_size(dbenv))) != 0)
00064                 goto err;
00065 
00066         /* If we created the region, initialize it. */
00067         if (F_ISSET(&mgr->reginfo, REGION_CREATE))
00068                 if ((ret = __txn_init(dbenv, mgr)) != 0)
00069                         goto err;
00070 
00071         /* Set the local addresses. */
00072         mgr->reginfo.primary =
00073             R_ADDR(&mgr->reginfo, mgr->reginfo.rp->primary);
00074 
00075         /* If threaded, acquire a mutex to protect the active TXN list. */
00076         if ((ret = __mutex_alloc(
00077             dbenv, MTX_TXN_ACTIVE, DB_MUTEX_THREAD, &mgr->mutex)) != 0)
00078                 goto err;
00079 
00080         dbenv->tx_handle = mgr;
00081         return (0);
00082 
00083 err:    dbenv->tx_handle = NULL;
00084         if (mgr->reginfo.addr != NULL)
00085                 (void)__db_r_detach(dbenv, &mgr->reginfo, 0);
00086 
00087         (void)__mutex_free(dbenv, &mgr->mutex);
00088         __os_free(dbenv, mgr);
00089         return (ret);
00090 }
00091 
00092 /*
00093  * __txn_init --
00094  *      Initialize a transaction region in shared memory.
00095  */
00096 static int
00097 __txn_init(dbenv, mgr)
00098         DB_ENV *dbenv;
00099         DB_TXNMGR *mgr;
00100 {
00101         DB_LSN last_ckp;
00102         DB_TXNREGION *region;
00103         int ret;
00104 
00105         /*
00106          * Find the last checkpoint in the log.
00107          */
00108         ZERO_LSN(last_ckp);
00109         if (LOGGING_ON(dbenv)) {
00110                 /*
00111                  * The log system has already walked through the last
00112                  * file.  Get the LSN of a checkpoint it may have found.
00113                  */
00114                 if ((ret = __log_get_cached_ckp_lsn(dbenv, &last_ckp)) != 0)
00115                         return (ret);
00116 
00117                 /*
00118                  * If that didn't work, look backwards from the beginning of
00119                  * the last log file until we find the last checkpoint.
00120                  */
00121                 if (IS_ZERO_LSN(last_ckp) &&
00122                     (ret = __txn_findlastckp(dbenv, &last_ckp, NULL)) != 0)
00123                         return (ret);
00124         }
00125 
00126         if ((ret = __db_shalloc(&mgr->reginfo,
00127             sizeof(DB_TXNREGION), 0, &mgr->reginfo.primary)) != 0) {
00128                 __db_err(dbenv,
00129                     "Unable to allocate memory for the transaction region");
00130                 return (ret);
00131         }
00132         mgr->reginfo.rp->primary =
00133             R_OFFSET(&mgr->reginfo, mgr->reginfo.primary);
00134         region = mgr->reginfo.primary;
00135         memset(region, 0, sizeof(*region));
00136 
00137         if ((ret = __mutex_alloc(
00138             dbenv, MTX_TXN_REGION, 0, &region->mtx_region)) != 0)
00139                 return (ret);
00140 
00141         region->maxtxns = dbenv->tx_max;
00142         region->last_txnid = TXN_MINIMUM;
00143         region->cur_maxid = TXN_MAXIMUM;
00144 
00145         if ((ret = __mutex_alloc(
00146             dbenv, MTX_TXN_CHKPT, 0, &region->mtx_ckp)) != 0)
00147                 return (ret);
00148         region->last_ckp = last_ckp;
00149         region->time_ckp = time(NULL);
00150 
00151         memset(&region->stat, 0, sizeof(region->stat));
00152         region->stat.st_maxtxns = region->maxtxns;
00153 
00154         SH_TAILQ_INIT(&region->active_txn);
00155         return (ret);
00156 }
00157 
00158 /*
00159  * __txn_findlastckp --
00160  *      Find the last checkpoint in the log, walking backwards from the
00161  *      max_lsn given or the beginning of the last log file.  (The
00162  *      log system looked through the last log file when it started up.)
00163  *
00164  * PUBLIC: int __txn_findlastckp __P((DB_ENV *, DB_LSN *, DB_LSN *));
00165  */
00166 int
00167 __txn_findlastckp(dbenv, lsnp, max_lsn)
00168         DB_ENV *dbenv;
00169         DB_LSN *lsnp;
00170         DB_LSN *max_lsn;
00171 {
00172         DB_LOGC *logc;
00173         DB_LSN lsn;
00174         DBT dbt;
00175         int ret, t_ret;
00176         u_int32_t rectype;
00177 
00178         ZERO_LSN(*lsnp);
00179 
00180         if ((ret = __log_cursor(dbenv, &logc)) != 0)
00181                 return (ret);
00182 
00183         /* Get the last LSN. */
00184         memset(&dbt, 0, sizeof(dbt));
00185         if (max_lsn != NULL) {
00186                 lsn = *max_lsn;
00187                 if ((ret = __log_c_get(logc, &lsn, &dbt, DB_SET)) != 0)
00188                         goto err;
00189         } else {
00190                 if ((ret = __log_c_get(logc, &lsn, &dbt, DB_LAST)) != 0)
00191                         goto err;
00192                 /*
00193                  * Twiddle the last LSN so it points to the beginning of the
00194                  * last file; we know there's no checkpoint after that, since
00195                  * the log system already looked there.
00196                  */
00197                 lsn.offset = 0;
00198         }
00199 
00200         /* Read backwards, looking for checkpoints. */
00201         while ((ret = __log_c_get(logc, &lsn, &dbt, DB_PREV)) == 0) {
00202                 if (dbt.size < sizeof(u_int32_t))
00203                         continue;
00204                 memcpy(&rectype, dbt.data, sizeof(u_int32_t));
00205                 if (rectype == DB___txn_ckp) {
00206                         *lsnp = lsn;
00207                         break;
00208                 }
00209         }
00210 
00211 err:    if ((t_ret = __log_c_close(logc)) != 0 && ret == 0)
00212                 ret = t_ret;
00213 
00214         /*
00215          * Not finding a checkpoint is not an error;  there may not exist
00216          * one in the log.
00217          */
00218         return ((ret == 0 || ret == DB_NOTFOUND) ? 0 : ret);
00219 }
00220 
00221 /*
00222  * __txn_dbenv_refresh --
00223  *      Clean up after the transaction system on a close or failed open.
00224  *
00225  * PUBLIC: int __txn_dbenv_refresh __P((DB_ENV *));
00226  */
00227 int
00228 __txn_dbenv_refresh(dbenv)
00229         DB_ENV *dbenv;
00230 {
00231         DB_TXN *txn;
00232         DB_TXNMGR *mgr;
00233         REGINFO *reginfo;
00234         u_int32_t txnid;
00235         int aborted, ret, t_ret;
00236 
00237         ret = 0;
00238         mgr = dbenv->tx_handle;
00239         reginfo = &mgr->reginfo;
00240 
00241         /*
00242          * This function can only be called once per process (i.e., not
00243          * once per thread), so no synchronization is required.
00244          *
00245          * The caller is probably doing something wrong if close is called with
00246          * active transactions.  Try and abort any active transactions that are
00247          * not prepared, but it's quite likely the aborts will fail because
00248          * recovery won't find open files.  If we can't abort any of the
00249          * unprepared transaction, panic, we have to run recovery to get back
00250          * to a known state.
00251          */
00252         aborted = 0;
00253         if (TAILQ_FIRST(&mgr->txn_chain) != NULL) {
00254                 while ((txn = TAILQ_FIRST(&mgr->txn_chain)) != NULL) {
00255                         /* Prepared transactions are OK. */
00256                         txnid = txn->txnid;
00257                         if (((TXN_DETAIL *)txn->td)->status == TXN_PREPARED) {
00258                                 if ((ret = __txn_discard_int(txn, 0)) != 0) {
00259                                         __db_err(dbenv,
00260                                             "Unable to discard txn 0x%x: %s",
00261                                             txnid, db_strerror(ret));
00262                                         break;
00263                                 }
00264                                 continue;
00265                         }
00266                         aborted = 1;
00267                         if ((t_ret = __txn_abort(txn)) != 0) {
00268                                 __db_err(dbenv,
00269                                     "Unable to abort transaction 0x%x: %s",
00270                                     txnid, db_strerror(t_ret));
00271                                 ret = __db_panic(dbenv, t_ret);
00272                                 break;
00273                         }
00274                 }
00275                 if (aborted) {
00276                         __db_err(dbenv,
00277         "Error: closing the transaction region with active transactions");
00278                         if (ret == 0)
00279                                 ret = EINVAL;
00280                 }
00281         }
00282 
00283         /* Flush the log. */
00284         if (LOGGING_ON(dbenv) &&
00285             (t_ret = __log_flush(dbenv, NULL)) != 0 && ret == 0)
00286                 ret = t_ret;
00287 
00288         /* Discard the per-thread lock. */
00289         if ((t_ret = __mutex_free(dbenv, &mgr->mutex)) != 0 && ret == 0)
00290                 ret = t_ret;
00291 
00292         /* Detach from the region. */
00293         if ((t_ret = __db_r_detach(dbenv, reginfo, 0)) != 0 && ret == 0)
00294                 ret = t_ret;
00295 
00296         __os_free(dbenv, mgr);
00297 
00298         dbenv->tx_handle = NULL;
00299         return (ret);
00300 }
00301 
00302 /*
00303  * __txn_region_size --
00304  *       Return the amount of space needed for the txn region.  Make the
00305  *       region large enough to hold txn_max transaction detail structures
00306  *       plus some space to hold thread handles and the beginning of the
00307  *       shalloc region and anything we need for mutex system resource
00308  *       recording.
00309  */
00310 static size_t
00311 __txn_region_size(dbenv)
00312         DB_ENV *dbenv;
00313 {
00314         size_t s;
00315 
00316         s = sizeof(DB_TXNREGION) +
00317             dbenv->tx_max * sizeof(TXN_DETAIL) + 10 * 1024;
00318         return (s);
00319 }
00320 
00321 /*
00322  * __txn_id_set --
00323  *      Set the current transaction ID and current maximum unused ID (for
00324  *      testing purposes only).
00325  *
00326  * PUBLIC: int __txn_id_set __P((DB_ENV *, u_int32_t, u_int32_t));
00327  */
00328 int
00329 __txn_id_set(dbenv, cur_txnid, max_txnid)
00330         DB_ENV *dbenv;
00331         u_int32_t cur_txnid, max_txnid;
00332 {
00333         DB_TXNMGR *mgr;
00334         DB_TXNREGION *region;
00335         int ret;
00336 
00337         ENV_REQUIRES_CONFIG(dbenv, dbenv->tx_handle, "txn_id_set", DB_INIT_TXN);
00338 
00339         mgr = dbenv->tx_handle;
00340         region = mgr->reginfo.primary;
00341         region->last_txnid = cur_txnid;
00342         region->cur_maxid = max_txnid;
00343 
00344         ret = 0;
00345         if (cur_txnid < TXN_MINIMUM) {
00346                 __db_err(dbenv, "Current ID value %lu below minimum",
00347                     (u_long)cur_txnid);
00348                 ret = EINVAL;
00349         }
00350         if (max_txnid < TXN_MINIMUM) {
00351                 __db_err(dbenv, "Maximum ID value %lu below minimum",
00352                     (u_long)max_txnid);
00353                 ret = EINVAL;
00354         }
00355         return (ret);
00356 }

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