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

txn_util.c

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 2001-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: txn_util.c,v 12.2 2005/09/28 17:45:20 margo Exp $
00008  */
00009 
00010 #include "db_config.h"
00011 
00012 #ifndef NO_SYSTEM_INCLUDES
00013 #include <sys/types.h>
00014 #include <string.h>
00015 #endif
00016 
00017 #include "db_int.h"
00018 #include "dbinc/db_page.h"
00019 #include "dbinc/db_shash.h"
00020 #include "dbinc/lock.h"
00021 #include "dbinc/mp.h"
00022 #include "dbinc/txn.h"
00023 #include "dbinc/db_am.h"
00024 
00025 typedef struct __txn_event TXN_EVENT;
00026 struct __txn_event {
00027         TXN_EVENT_T op;
00028         TAILQ_ENTRY(__txn_event) links;
00029         union {
00030                 struct {
00031                         /* Delayed close. */
00032                         DB *dbp;
00033                 } c;
00034                 struct {
00035                         /* Delayed remove. */
00036                         char *name;
00037                         u_int8_t *fileid;
00038                         int inmem;
00039                 } r;
00040                 struct {
00041                         /* Lock event. */
00042                         DB_LOCK lock;
00043                         u_int32_t locker;
00044                         DB *dbp;
00045                 } t;
00046         } u;
00047 };
00048 
00049 /*
00050  * __txn_closeevent --
00051  *
00052  * Creates a close event that can be added to the [so-called] commit list, so
00053  * that we can redo a failed DB handle close once we've aborted the transaction.
00054  *
00055  * PUBLIC: int __txn_closeevent __P((DB_ENV *, DB_TXN *, DB *));
00056  */
00057 int
00058 __txn_closeevent(dbenv, txn, dbp)
00059         DB_ENV *dbenv;
00060         DB_TXN *txn;
00061         DB *dbp;
00062 {
00063         int ret;
00064         TXN_EVENT *e;
00065 
00066         e = NULL;
00067         if ((ret = __os_calloc(dbenv, 1, sizeof(TXN_EVENT), &e)) != 0)
00068                 return (ret);
00069 
00070         e->u.c.dbp = dbp;
00071         e->op = TXN_CLOSE;
00072         TAILQ_INSERT_TAIL(&txn->events, e, links);
00073 
00074         return (0);
00075 }
00076 
00077 /*
00078  * __txn_remevent --
00079  *
00080  * Creates a remove event that can be added to the commit list.
00081  *
00082  * PUBLIC: int __txn_remevent __P((DB_ENV *,
00083  * PUBLIC:       DB_TXN *, const char *, u_int8_t *, int));
00084  */
00085 int
00086 __txn_remevent(dbenv, txn, name, fileid, inmem)
00087         DB_ENV *dbenv;
00088         DB_TXN *txn;
00089         const char *name;
00090         u_int8_t *fileid;
00091         int inmem;
00092 {
00093         int ret;
00094         TXN_EVENT *e;
00095 
00096         e = NULL;
00097         if ((ret = __os_calloc(dbenv, 1, sizeof(TXN_EVENT), &e)) != 0)
00098                 return (ret);
00099 
00100         if ((ret = __os_strdup(dbenv, name, &e->u.r.name)) != 0)
00101                 goto err;
00102 
00103         if (fileid != NULL) {
00104                 if ((ret = __os_calloc(dbenv,
00105                     1, DB_FILE_ID_LEN, &e->u.r.fileid)) != 0)
00106                         return (ret);
00107                 memcpy(e->u.r.fileid, fileid, DB_FILE_ID_LEN);
00108         }
00109 
00110         e->u.r.inmem = inmem;
00111         e->op = TXN_REMOVE;
00112         TAILQ_INSERT_TAIL(&txn->events, e, links);
00113 
00114         return (0);
00115 
00116 err:    if (e != NULL)
00117                 __os_free(dbenv, e);
00118 
00119         return (ret);
00120 }
00121 
00122 /*
00123  * __txn_remrem --
00124  *      Remove a remove event because the remove has been superceeded,
00125  * by a create of the same name, for example.
00126  *
00127  * PUBLIC: void __txn_remrem __P((DB_ENV *, DB_TXN *, const char *));
00128  */
00129 void
00130 __txn_remrem(dbenv, txn, name)
00131         DB_ENV *dbenv;
00132         DB_TXN *txn;
00133         const char *name;
00134 {
00135         TXN_EVENT *e, *next_e;
00136 
00137         for (e = TAILQ_FIRST(&txn->events); e != NULL; e = next_e) {
00138                 next_e = TAILQ_NEXT(e, links);
00139                 if (e->op != TXN_REMOVE || strcmp(name, e->u.r.name) != 0)
00140                         continue;
00141                 TAILQ_REMOVE(&txn->events, e, links);
00142                 __os_free(dbenv, e->u.r.name);
00143                 if (e->u.r.fileid != NULL)
00144                         __os_free(dbenv, e->u.r.fileid);
00145                 __os_free(dbenv, e);
00146         }
00147 
00148         return;
00149 }
00150 
00151 /*
00152  * __txn_lockevent --
00153  *
00154  * Add a lockevent to the commit-queue.  The lock event indicates a locker
00155  * trade.
00156  *
00157  * PUBLIC: int __txn_lockevent __P((DB_ENV *,
00158  * PUBLIC:     DB_TXN *, DB *, DB_LOCK *, u_int32_t));
00159  */
00160 int
00161 __txn_lockevent(dbenv, txn, dbp, lock, locker)
00162         DB_ENV *dbenv;
00163         DB_TXN *txn;
00164         DB *dbp;
00165         DB_LOCK *lock;
00166         u_int32_t locker;
00167 {
00168         int ret;
00169         TXN_EVENT *e;
00170 
00171         if (!LOCKING_ON(dbenv))
00172                 return (0);
00173 
00174         e = NULL;
00175         if ((ret = __os_calloc(dbenv, 1, sizeof(TXN_EVENT), &e)) != 0)
00176                 return (ret);
00177 
00178         e->u.t.locker = locker;
00179         e->u.t.lock = *lock;
00180         e->u.t.dbp = dbp;
00181         e->op = TXN_TRADE;
00182         TAILQ_INSERT_TAIL(&txn->events, e, links);
00183 
00184         return (0);
00185 }
00186 
00187 /*
00188  * __txn_remlock --
00189  *      Remove a lock event because the locker is going away.  We can remove
00190  * by lock (using offset) or by locker_id (or by both).
00191  *
00192  * PUBLIC: void __txn_remlock __P((DB_ENV *, DB_TXN *, DB_LOCK *, u_int32_t));
00193  */
00194 void
00195 __txn_remlock(dbenv, txn, lock, locker)
00196         DB_ENV *dbenv;
00197         DB_TXN *txn;
00198         DB_LOCK *lock;
00199         u_int32_t locker;
00200 {
00201         TXN_EVENT *e, *next_e;
00202 
00203         for (e = TAILQ_FIRST(&txn->events); e != NULL; e = next_e) {
00204                 next_e = TAILQ_NEXT(e, links);
00205                 if ((e->op != TXN_TRADE && e->op != TXN_TRADED) ||
00206                     (e->u.t.lock.off != lock->off && e->u.t.locker != locker))
00207                         continue;
00208                 TAILQ_REMOVE(&txn->events, e, links);
00209                 __os_free(dbenv, e);
00210         }
00211 
00212         return;
00213 }
00214 
00215 /*
00216  * __txn_doevents --
00217  * Process the list of events associated with a transaction.  On commit,
00218  * apply the events; on abort, just toss the entries.
00219  *
00220  * PUBLIC: int __txn_doevents __P((DB_ENV *, DB_TXN *, int, int));
00221  */
00222 #define DO_TRADE do {                                                   \
00223         memset(&req, 0, sizeof(req));                                   \
00224         req.lock = e->u.t.lock;                                         \
00225         req.op = DB_LOCK_TRADE;                                         \
00226         t_ret = __lock_vec(dbenv, e->u.t.locker, 0, &req, 1, NULL);     \
00227         if (t_ret == 0)                                                 \
00228                 e->u.t.dbp->cur_lid = e->u.t.locker;                    \
00229         else if (t_ret == DB_NOTFOUND)                                  \
00230                 t_ret = 0;                                              \
00231         if (t_ret != 0 && ret == 0)                                     \
00232                 ret = t_ret;                                            \
00233         e->op = TXN_TRADED;                                             \
00234 } while (0)
00235 
00236 int
00237 __txn_doevents(dbenv, txn, opcode, preprocess)
00238         DB_ENV *dbenv;
00239         DB_TXN *txn;
00240         int opcode, preprocess;
00241 {
00242         DB_LOCKREQ req;
00243         TXN_EVENT *e;
00244         int ret, t_ret;
00245 
00246         ret = 0;
00247 
00248         /*
00249          * This phase only gets called if we have a phase where we
00250          * release read locks.  Since not all paths will call this
00251          * phase, we have to check for it below as well.  So, when
00252          * we do the trade, we update the opcode of the entry so that
00253          * we don't try the trade again.
00254          */
00255         if (preprocess) {
00256                 for (e = TAILQ_FIRST(&txn->events);
00257                     e != NULL; e = TAILQ_NEXT(e, links)) {
00258                         if (e->op != TXN_TRADE)
00259                                 continue;
00260                         DO_TRADE;
00261                 }
00262                 return (ret);
00263         }
00264 
00265         /*
00266          * Prepare should only cause a preprocess, since the transaction
00267          * isn't over.
00268          */
00269         DB_ASSERT(opcode != TXN_PREPARE);
00270         while ((e = TAILQ_FIRST(&txn->events)) != NULL) {
00271                 TAILQ_REMOVE(&txn->events, e, links);
00272                 /*
00273                  * Most deferred events should only happen on
00274                  * commits, not aborts or prepares.  The one exception
00275                  * is a close which gets done on commit and abort, but
00276                  * not prepare. If we're not doing operations, then we
00277                  * can just go free resources.
00278                  */
00279                 if (opcode == TXN_ABORT && e->op != TXN_CLOSE)
00280                         goto dofree;
00281                 switch (e->op) {
00282                 case TXN_CLOSE:
00283                         /* If we didn't abort this txn, we screwed up badly. */
00284                         DB_ASSERT(opcode == TXN_ABORT);
00285                         if ((t_ret = __db_close(e->u.c.dbp,
00286                             NULL, DB_NOSYNC)) != 0 && ret == 0)
00287                                 ret = t_ret;
00288                         break;
00289                 case TXN_REMOVE:
00290                         if (e->u.r.fileid != NULL) {
00291                                 if ((t_ret = __memp_nameop(dbenv,
00292                                     e->u.r.fileid, NULL, e->u.r.name,
00293                                     NULL, e->u.r.inmem)) != 0 && ret == 0)
00294                                         ret = t_ret;
00295                         } else if ((t_ret =
00296                             __os_unlink(dbenv, e->u.r.name)) != 0 && ret == 0)
00297                                 ret = t_ret;
00298                         break;
00299                 case TXN_TRADE:
00300                         DO_TRADE;
00301                         /* Fall through */
00302                 case TXN_TRADED:
00303                         /* Downgrade the lock. */
00304                         if ((t_ret = __lock_downgrade(dbenv,
00305                             &e->u.t.lock, DB_LOCK_READ, 0)) != 0 && ret == 0)
00306                                 ret = t_ret;
00307                         break;
00308                 default:
00309                         /* This had better never happen. */
00310                         DB_ASSERT(0);
00311                 }
00312 dofree:
00313                 /* Free resources here. */
00314                 switch (e->op) {
00315                 case TXN_REMOVE:
00316                         if (e->u.r.fileid != NULL)
00317                                 __os_free(dbenv, e->u.r.fileid);
00318                         __os_free(dbenv, e->u.r.name);
00319                         break;
00320                 case TXN_CLOSE:
00321                 case TXN_TRADE:
00322                 case TXN_TRADED:
00323                 default:
00324                         break;
00325                 }
00326                 __os_free(dbenv, e);
00327         }
00328 
00329         return (ret);
00330 }

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