1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
|
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2001-2004
* Sleepycat Software. All rights reserved.
*
* $Id: txn_util.c,v 11.28 2004/09/16 17:55:19 margo Exp $
*/
#include "db_config.h"
#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>
#include <string.h>
#endif
#include "db_int.h"
#include "dbinc/db_page.h"
#include "dbinc/db_shash.h"
#include "dbinc/lock.h"
#include "dbinc/mp.h"
#include "dbinc/txn.h"
#include "dbinc/db_am.h"
typedef struct __txn_event TXN_EVENT;
struct __txn_event {
TXN_EVENT_T op;
TAILQ_ENTRY(__txn_event) links;
union {
struct {
/* Delayed close. */
DB *dbp;
} c;
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_closeevent --
*
* Creates a close event that can be added to the [so-called] commit list, so
* that we can redo a failed DB handle close once we've aborted the transaction.
*
* PUBLIC: int __txn_closeevent __P((DB_ENV *, DB_TXN *, DB *));
*/
int
__txn_closeevent(dbenv, txn, dbp)
DB_ENV *dbenv;
DB_TXN *txn;
DB *dbp;
{
int ret;
TXN_EVENT *e;
e = NULL;
if ((ret = __os_calloc(dbenv, 1, sizeof(TXN_EVENT), &e)) != 0)
return (ret);
e->u.c.dbp = dbp;
e->op = TXN_CLOSE;
TAILQ_INSERT_TAIL(&txn->events, e, links);
return (0);
}
/*
* __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_remrem --
* Remove a remove event because the remove has be superceeded,
* by a create of the same name, for example.
*
* PUBLIC: void __txn_remrem __P((DB_ENV *, DB_TXN *, const char *));
*/
void
__txn_remrem(dbenv, txn, name)
DB_ENV *dbenv;
DB_TXN *txn;
const char *name;
{
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_REMOVE || strcmp(name, e->u.r.name) != 0)
continue;
TAILQ_REMOVE(&txn->events, e, links);
__os_free(dbenv, e->u.r.name);
if (e->u.r.fileid != NULL)
__os_free(dbenv, e->u.r.fileid);
__os_free(dbenv, e);
}
return;
}
/*
* __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, opcode, preprocess)
DB_ENV *dbenv;
DB_TXN *txn;
int opcode, 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);
}
/*
* Prepare should only cause a preprocess, since the transaction
* isn't over.
*/
DB_ASSERT(opcode != TXN_PREPARE);
while ((e = TAILQ_FIRST(&txn->events)) != NULL) {
TAILQ_REMOVE(&txn->events, e, links);
/*
* Most deferred events should only happen on
* commits, not aborts or prepares. The one exception
* is a close which gets done on commit and abort, but
* not prepare. If we're not doing operations, then we
* can just go free resources.
*/
if (opcode == TXN_ABORT && e->op != TXN_CLOSE)
goto dofree;
switch (e->op) {
case TXN_CLOSE:
/* If we didn't abort this txn, we screwed up badly. */
DB_ASSERT(opcode == TXN_ABORT);
if ((t_ret = __db_close(e->u.c.dbp,
NULL, DB_NOSYNC)) != 0 && ret == 0)
ret = t_ret;
break;
case TXN_REMOVE:
if (e->u.r.fileid != NULL) {
if ((t_ret = __memp_nameop(dbenv,
e->u.r.fileid,
NULL, e->u.r.name, NULL)) != 0 && ret == 0)
ret = t_ret;
} else if ((t_ret =
__os_unlink(dbenv, e->u.r.name)) != 0 && ret == 0)
ret = t_ret;
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:
/* Free resources here. */
switch (e->op) {
case TXN_REMOVE:
if (e->u.r.fileid != NULL)
__os_free(dbenv, e->u.r.fileid);
__os_free(dbenv, e->u.r.name);
break;
case TXN_CLOSE:
case TXN_TRADE:
case TXN_TRADED:
default:
break;
}
__os_free(dbenv, e);
}
return (ret);
}
|