/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2001-2002 * Sleepycat Software. All rights reserved. */ #include "db_config.h" #ifndef lint static const char revid[] = "$Id: txn_util.c,v 1.1.1.1 2003/02/15 04:56:20 zarzycki Exp $"; #endif /* not lint */ #ifndef NO_SYSTEM_INCLUDES #include #include #endif #include "db_int.h" #include "dbinc/db_shash.h" #include "dbinc/lock.h" #include "dbinc/txn.h" typedef struct __txn_event TXN_EVENT; struct __txn_event { TXN_EVENT_T op; TAILQ_ENTRY(__txn_event) links; union { struct { /* Delayed remove. */ char *name; u_int8_t *fileid; } r; struct { /* Lock event. */ DB_LOCK lock; u_int32_t locker; DB *dbp; } t; } u; }; /* * __txn_remevent -- * * Creates a remove event that can be added to the commit list. * * PUBLIC: int __txn_remevent __P((DB_ENV *, * PUBLIC: DB_TXN *, const char *, u_int8_t*)); */ int __txn_remevent(dbenv, txn, name, fileid) DB_ENV *dbenv; DB_TXN *txn; const char *name; u_int8_t *fileid; { int ret; TXN_EVENT *e; e = NULL; if ((ret = __os_calloc(dbenv, 1, sizeof(TXN_EVENT), &e)) != 0) return (ret); if ((ret = __os_strdup(dbenv, name, &e->u.r.name)) != 0) goto err; if (fileid != NULL) { if ((ret = __os_calloc(dbenv, 1, DB_FILE_ID_LEN, &e->u.r.fileid)) != 0) return (ret); memcpy(e->u.r.fileid, fileid, DB_FILE_ID_LEN); } e->op = TXN_REMOVE; TAILQ_INSERT_TAIL(&txn->events, e, links); return (0); err: if (e != NULL) __os_free(dbenv, e); return (ret); } /* * __txn_lockevent -- * * Add a lockevent to the commit-queue. The lock event indicates a locker * trade. * * PUBLIC: int __txn_lockevent __P((DB_ENV *, * PUBLIC: DB_TXN *, DB *, DB_LOCK *, u_int32_t)); */ int __txn_lockevent(dbenv, txn, dbp, lock, locker) DB_ENV *dbenv; DB_TXN *txn; DB *dbp; DB_LOCK *lock; u_int32_t locker; { int ret; TXN_EVENT *e; if (!LOCKING_ON(dbenv)) return (0); e = NULL; if ((ret = __os_calloc(dbenv, 1, sizeof(TXN_EVENT), &e)) != 0) return (ret); e->u.t.locker = locker; e->u.t.lock = *lock; e->u.t.dbp = dbp; e->op = TXN_TRADE; TAILQ_INSERT_TAIL(&txn->events, e, links); return (0); } /* * __txn_remlock -- * Remove a lock event because the locker is going away. We can remove * by lock (using offset) or by locker_id (or by both). * * PUBLIC: void __txn_remlock __P((DB_ENV *, DB_TXN *, DB_LOCK *, u_int32_t)); */ void __txn_remlock(dbenv, txn, lock, locker) DB_ENV *dbenv; DB_TXN *txn; DB_LOCK *lock; u_int32_t locker; { TXN_EVENT *e, *next_e; for (e = TAILQ_FIRST(&txn->events); e != NULL; e = next_e) { next_e = TAILQ_NEXT(e, links); if ((e->op != TXN_TRADE && e->op != TXN_TRADED) || (e->u.t.lock.off != lock->off && e->u.t.locker != locker)) continue; TAILQ_REMOVE(&txn->events, e, links); __os_free(dbenv, e); } return; } /* * __txn_doevents -- * Process the list of events associated with a transaction. On commit, * apply the events; on abort, just toss the entries. * * PUBLIC: int __txn_doevents __P((DB_ENV *, DB_TXN *, int, int)); */ #define DO_TRADE do { \ memset(&req, 0, sizeof(req)); \ req.lock = e->u.t.lock; \ req.op = DB_LOCK_TRADE; \ t_ret = __lock_vec(dbenv, e->u.t.locker, 0, &req, 1, NULL); \ if (t_ret == 0) \ e->u.t.dbp->cur_lid = e->u.t.locker; \ else if (t_ret == DB_NOTFOUND) \ t_ret = 0; \ if (t_ret != 0 && ret == 0) \ ret = t_ret; \ e->op = TXN_TRADED; \ } while (0) int __txn_doevents(dbenv, txn, is_commit, preprocess) DB_ENV *dbenv; DB_TXN *txn; int is_commit, preprocess; { DB_LOCKREQ req; TXN_EVENT *e; int ret, t_ret; ret = 0; /* * This phase only gets called if we have a phase where we * release read locks. Since not all paths will call this * phase, we have to check for it below as well. So, when * we do the trade, we update the opcode of the entry so that * we don't try the trade again. */ if (preprocess) { for (e = TAILQ_FIRST(&txn->events); e != NULL; e = TAILQ_NEXT(e, links)) { if (e->op != TXN_TRADE) continue; DO_TRADE; } return (ret); } while ((e = TAILQ_FIRST(&txn->events)) != NULL) { TAILQ_REMOVE(&txn->events, e, links); if (!is_commit) goto dofree; switch (e->op) { case TXN_REMOVE: if (e->u.r.fileid != NULL) { if ((t_ret = dbenv->memp_nameop(dbenv, e->u.r.fileid, NULL, e->u.r.name, NULL)) != 0 && ret == 0) ret = t_ret; __os_free(dbenv, e->u.r.fileid); } else if ((t_ret = __os_unlink(dbenv, e->u.r.name)) != 0 && ret == 0) ret = t_ret; __os_free(dbenv, e->u.r.name); break; case TXN_TRADE: DO_TRADE; /* Fall through */ case TXN_TRADED: /* Downgrade the lock. */ if ((t_ret = __lock_downgrade(dbenv, &e->u.t.lock, DB_LOCK_READ, 0)) != 0 && ret == 0) ret = t_ret; break; default: /* This had better never happen. */ DB_ASSERT(0); } dofree: __os_free(dbenv, e); } return (ret); }