2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 2001, 2010 Oracle and/or its affiliates. All rights reserved.
12 #include "dbinc/db_page.h"
13 #include "dbinc/lock.h"
15 #include "dbinc/txn.h"
16 #include "dbinc/log.h"
17 #include "dbinc/db_am.h"
19 typedef struct __txn_event TXN_EVENT;
22 TAILQ_ENTRY(__txn_event) links;
43 #define TXN_TOP_PARENT(txn) do { \
44 while (txn->parent != NULL) \
51 * Creates a close event that can be added to the [so-called] commit list, so
52 * that we can redo a failed DB handle close once we've aborted the transaction.
54 * PUBLIC: int __txn_closeevent __P((ENV *, DB_TXN *, DB *));
57 __txn_closeevent(env, txn, dbp)
66 if ((ret = __os_calloc(env, 1, sizeof(TXN_EVENT), &e)) != 0)
72 TAILQ_INSERT_TAIL(&txn->events, e, links);
80 * Creates a remove event that can be added to the commit list.
82 * PUBLIC: int __txn_remevent __P((ENV *,
83 * PUBLIC: DB_TXN *, const char *, u_int8_t *, int));
86 __txn_remevent(env, txn, name, fileid, inmem)
97 if ((ret = __os_calloc(env, 1, sizeof(TXN_EVENT), &e)) != 0)
100 if ((ret = __os_strdup(env, name, &e->u.r.name)) != 0)
103 if (fileid != NULL) {
104 if ((ret = __os_calloc(env,
105 1, DB_FILE_ID_LEN, &e->u.r.fileid)) != 0)
107 memcpy(e->u.r.fileid, fileid, DB_FILE_ID_LEN);
110 e->u.r.inmem = inmem;
113 TAILQ_INSERT_TAIL(&txn->events, e, links);
125 * Remove a remove event because the remove has been superceeded,
126 * by a create of the same name, for example.
128 * PUBLIC: void __txn_remrem __P((ENV *, DB_TXN *, const char *));
131 __txn_remrem(env, txn, name)
136 TXN_EVENT *e, *next_e;
139 for (e = TAILQ_FIRST(&txn->events); e != NULL; e = next_e) {
140 next_e = TAILQ_NEXT(e, links);
141 if (e->op != TXN_REMOVE || strcmp(name, e->u.r.name) != 0)
143 TAILQ_REMOVE(&txn->events, e, links);
144 __os_free(env, e->u.r.name);
145 if (e->u.r.fileid != NULL)
146 __os_free(env, e->u.r.fileid);
156 * Add a lockevent to the commit-queue. The lock event indicates a locker
159 * PUBLIC: int __txn_lockevent __P((ENV *,
160 * PUBLIC: DB_TXN *, DB *, DB_LOCK *, DB_LOCKER *));
163 __txn_lockevent(env, txn, dbp, lock, locker)
173 if (!LOCKING_ON(env))
177 if ((ret = __os_calloc(env, 1, sizeof(TXN_EVENT), &e)) != 0)
180 e->u.t.locker = locker;
184 /* This event goes on the current transaction, not its parent. */
185 TAILQ_INSERT_TAIL(&txn->events, e, links);
193 * Remove a lock event because the locker is going away. We can remove
194 * by lock (using offset) or by locker_id (or by both).
196 * PUBLIC: void __txn_remlock __P((ENV *, DB_TXN *, DB_LOCK *, DB_LOCKER *));
199 __txn_remlock(env, txn, lock, locker)
205 TXN_EVENT *e, *next_e;
207 for (e = TAILQ_FIRST(&txn->events); e != NULL; e = next_e) {
208 next_e = TAILQ_NEXT(e, links);
209 if ((e->op != TXN_TRADE && e->op != TXN_TRADED) ||
210 (e->u.t.lock.off != lock->off && e->u.t.locker != locker))
212 TAILQ_REMOVE(&txn->events, e, links);
221 * Process the list of events associated with a transaction. On commit,
222 * apply the events; on abort, just toss the entries.
224 * PUBLIC: int __txn_doevents __P((ENV *, DB_TXN *, int, int));
226 #define DO_TRADE do { \
227 memset(&req, 0, sizeof(req)); \
228 req.lock = e->u.t.lock; \
229 req.op = DB_LOCK_TRADE; \
230 t_ret = __lock_vec(env, txn->parent ? \
231 txn->parent->locker : e->u.t.locker, 0, &req, 1, NULL); \
233 if (txn->parent != NULL) { \
234 e->u.t.dbp->cur_txn = txn->parent; \
235 e->u.t.dbp->cur_locker = txn->parent->locker; \
237 e->op = TXN_TRADED; \
238 e->u.t.dbp->cur_locker = e->u.t.locker; \
239 e->u.t.dbp->cur_txn = NULL; \
241 } else if (t_ret == DB_NOTFOUND) \
243 if (t_ret != 0 && ret == 0) \
248 __txn_doevents(env, txn, opcode, preprocess)
251 int opcode, preprocess;
254 TXN_EVENT *e, *enext;
260 * This phase only gets called if we have a phase where we
261 * release read locks. Since not all paths will call this
262 * phase, we have to check for it below as well. So, when
263 * we do the trade, we update the opcode of the entry so that
264 * we don't try the trade again.
267 for (e = TAILQ_FIRST(&txn->events);
268 e != NULL; e = enext) {
269 enext = TAILQ_NEXT(e, links);
270 if (e->op != TXN_TRADE ||
271 IS_WRITELOCK(e->u.t.lock.mode))
274 if (txn->parent != NULL) {
275 TAILQ_REMOVE(&txn->events, e, links);
277 &txn->parent->events, e, links);
284 * Prepare should only cause a preprocess, since the transaction
287 DB_ASSERT(env, opcode != TXN_PREPARE);
288 while ((e = TAILQ_FIRST(&txn->events)) != NULL) {
289 TAILQ_REMOVE(&txn->events, e, links);
291 * Most deferred events should only happen on
292 * commits, not aborts or prepares. The one exception
293 * is a close which gets done on commit and abort, but
294 * not prepare. If we're not doing operations, then we
295 * can just go free resources.
297 if (opcode == TXN_ABORT && e->op != TXN_CLOSE)
301 if ((t_ret = __db_close(e->u.c.dbp,
302 NULL, DB_NOSYNC)) != 0 && ret == 0)
306 if (e->u.r.fileid != NULL) {
307 if ((t_ret = __memp_nameop(env,
308 e->u.r.fileid, NULL, e->u.r.name,
309 NULL, e->u.r.inmem)) != 0 && ret == 0)
312 __os_unlink(env, e->u.r.name, 0)) != 0 && ret == 0)
317 if (txn->parent != NULL) {
319 &txn->parent->events, e, links);
324 /* Downgrade the lock. */
325 if ((t_ret = __lock_downgrade(env,
326 &e->u.t.lock, DB_LOCK_READ, 0)) != 0 && ret == 0)
330 /* This had better never happen. */
334 /* Free resources here. */
337 if (e->u.r.fileid != NULL)
338 __os_free(env, e->u.r.fileid);
339 __os_free(env, e->u.r.name);
342 if (opcode == TXN_ABORT)
343 e->u.t.dbp->cur_txn = NULL;
357 * PUBLIC: int __txn_record_fname __P((ENV *, DB_TXN *, FNAME *));
360 __txn_record_fname(env, txn, fname)
373 if ((td = txn->td) == NULL)
375 mgr = env->tx_handle;
376 dblp = env->lg_handle;
377 fname_off = R_OFFSET(&dblp->reginfo, fname);
379 /* See if we already have a ref to this DB handle. */
380 ldbs = R_ADDR(&mgr->reginfo, td->log_dbs);
381 for (i = 0, np = ldbs; i < td->nlog_dbs; i++, np++)
382 if (*np == fname_off)
385 if (td->nlog_slots <= td->nlog_dbs) {
386 TXN_SYSTEM_LOCK(env);
387 if ((ret = __env_alloc(&mgr->reginfo,
388 sizeof(roff_t) * (td->nlog_slots << 1), &np)) != 0) {
389 TXN_SYSTEM_UNLOCK(env);
393 memcpy(np, ldbs, td->nlog_dbs * sizeof(roff_t));
394 if (td->nlog_slots > TXN_NSLOTS)
395 __env_alloc_free(&mgr->reginfo, ldbs);
397 TXN_SYSTEM_UNLOCK(env);
398 td->log_dbs = R_OFFSET(&mgr->reginfo, np);
400 td->nlog_slots = td->nlog_slots << 1;
403 ldbs[td->nlog_dbs] = fname_off;
412 * Either pass the fname to our parent txn or decrement the refcount
413 * and close the fileid if it goes to zero.
415 * PUBLIC: int __txn_dref_fname __P((ENV *, DB_TXN *));
418 __txn_dref_fname(env, txn)
426 TXN_DETAIL *ptd, *td;
432 if (td->nlog_dbs == 0)
435 mgr = env->tx_handle;
436 dblp = env->lg_handle;
439 ptd = txn->parent != NULL ? txn->parent->td : NULL;
441 np = R_ADDR(&mgr->reginfo, td->log_dbs);
442 for (i = 0; i < td->nlog_dbs; i++, np++) {
443 fname = R_ADDR(&dblp->reginfo, *np);
444 MUTEX_LOCK(env, fname->mutex);
446 ret = __txn_record_fname(env, txn->parent, fname);
448 MUTEX_UNLOCK(env, fname->mutex);
449 } else if (fname->txn_ref == 1) {
450 MUTEX_UNLOCK(env, fname->mutex);
451 DB_ASSERT(env, fname->txn_ref != 0);
452 ret = __dbreg_close_id_int(
453 env, fname, DBREG_CLOSE, 0);
456 MUTEX_UNLOCK(env, fname->mutex);