Update.
[glibc.git] / db2 / txn / txn.c
blob4f3ffd8ed2e520d1665241505e44fe1a7c05e72f
1 /*-
2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
6 */
7 /*
8 * Copyright (c) 1995, 1996
9 * The President and Fellows of Harvard University. All rights reserved.
11 * This code is derived from software contributed to Berkeley by
12 * Margo Seltzer.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the University of
25 * California, Berkeley and its contributors.
26 * 4. Neither the name of the University nor the names of its contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
43 #include "config.h"
45 #ifndef lint
46 static const char sccsid[] = "@(#)txn.c 10.58 (Sleepycat) 5/31/98";
47 #endif /* not lint */
50 #ifndef NO_SYSTEM_INCLUDES
51 #include <sys/types.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <time.h>
56 #endif
58 #include "db_int.h"
59 #include "shqueue.h"
60 #include "db_page.h"
61 #include "db_shash.h"
62 #include "txn.h"
63 #include "db_dispatch.h"
64 #include "lock.h"
65 #include "log.h"
66 #include "db_am.h"
67 #include "common_ext.h"
69 static int __txn_check_running __P((const DB_TXN *));
70 static int __txn_end __P((DB_TXN *, int));
71 static int __txn_grow_region __P((DB_TXNMGR *));
72 static int __txn_init __P((DB_TXNREGION *));
73 static int __txn_undo __P((DB_TXN *));
74 static int __txn_validate_region __P((DB_TXNMGR *));
77 * This file contains the top level routines of the transaction library.
78 * It assumes that a lock manager and log manager that conform to the db_log(3)
79 * and db_lock(3) interfaces exist.
81 * Initialize a transaction region in shared memory.
82 * Return 0 on success, errno on failure.
84 static int
85 __txn_init(txn_region)
86 DB_TXNREGION *txn_region;
88 time_t now;
90 (void)time(&now);
92 /* maxtxns is already initialized. */
93 txn_region->magic = DB_TXNMAGIC;
94 txn_region->version = DB_TXNVERSION;
95 txn_region->last_txnid = TXN_MINIMUM;
96 /* XXX If we ever do more types of locking and logging, this changes. */
97 txn_region->logtype = 0;
98 txn_region->locktype = 0;
99 txn_region->time_ckp = now;
100 ZERO_LSN(txn_region->last_ckp);
101 ZERO_LSN(txn_region->pending_ckp);
102 SH_TAILQ_INIT(&txn_region->active_txn);
103 __db_shalloc_init((void *)&txn_region[1],
104 TXN_REGION_SIZE(txn_region->maxtxns) - sizeof(DB_TXNREGION));
106 return (0);
110 txn_open(path, flags, mode, dbenv, mgrpp)
111 const char *path;
112 u_int32_t flags;
113 int mode;
114 DB_ENV *dbenv;
115 DB_TXNMGR **mgrpp;
117 DB_TXNMGR *tmgrp;
118 u_int32_t maxtxns;
119 int ret;
121 /* Validate arguments. */
122 if (dbenv == NULL)
123 return (EINVAL);
124 #ifdef HAVE_SPINLOCKS
125 #define OKFLAGS (DB_CREATE | DB_THREAD | DB_TXN_NOSYNC)
126 #else
127 #define OKFLAGS (DB_CREATE | DB_TXN_NOSYNC)
128 #endif
129 if ((ret = __db_fchk(dbenv, "txn_open", flags, OKFLAGS)) != 0)
130 return (ret);
132 maxtxns = dbenv->tx_max != 0 ? dbenv->tx_max : 20;
134 /* Now, create the transaction manager structure and set its fields. */
135 if ((tmgrp = (DB_TXNMGR *)__db_calloc(1, sizeof(DB_TXNMGR))) == NULL) {
136 __db_err(dbenv, "txn_open: %s", strerror(ENOMEM));
137 return (ENOMEM);
140 /* Initialize the transaction manager structure. */
141 tmgrp->mutexp = NULL;
142 tmgrp->dbenv = dbenv;
143 tmgrp->recover =
144 dbenv->tx_recover == NULL ? __db_dispatch : dbenv->tx_recover;
145 tmgrp->flags = LF_ISSET(DB_TXN_NOSYNC | DB_THREAD);
146 TAILQ_INIT(&tmgrp->txn_chain);
148 /* Join/create the txn region. */
149 tmgrp->reginfo.dbenv = dbenv;
150 tmgrp->reginfo.appname = DB_APP_NONE;
151 if (path == NULL)
152 tmgrp->reginfo.path = NULL;
153 else
154 if ((tmgrp->reginfo.path = (char *)__db_strdup(path)) == NULL)
155 goto err;
156 tmgrp->reginfo.file = DEFAULT_TXN_FILE;
157 tmgrp->reginfo.mode = mode;
158 tmgrp->reginfo.size = TXN_REGION_SIZE(maxtxns);
159 tmgrp->reginfo.dbflags = flags;
160 tmgrp->reginfo.addr = NULL;
161 tmgrp->reginfo.fd = -1;
162 tmgrp->reginfo.flags = dbenv->tx_max == 0 ? REGION_SIZEDEF : 0;
163 if ((ret = __db_rattach(&tmgrp->reginfo)) != 0)
164 goto err;
166 /* Fill in region-related fields. */
167 tmgrp->region = tmgrp->reginfo.addr;
168 tmgrp->mem = &tmgrp->region[1];
170 if (F_ISSET(&tmgrp->reginfo, REGION_CREATED)) {
171 tmgrp->region->maxtxns = maxtxns;
172 if ((ret = __txn_init(tmgrp->region)) != 0)
173 goto err;
175 } else if (tmgrp->region->magic != DB_TXNMAGIC) {
176 /* Check if valid region. */
177 __db_err(dbenv, "txn_open: Bad magic number");
178 ret = EINVAL;
179 goto err;
182 if (LF_ISSET(DB_THREAD)) {
183 if ((ret = __db_shalloc(tmgrp->mem, sizeof(db_mutex_t),
184 MUTEX_ALIGNMENT, &tmgrp->mutexp)) == 0)
186 * Since we only get here if threading is turned on, we
187 * know that we have spinlocks, so the offset is going
188 * to be ignored. We put 0 here as a valid placeholder.
190 __db_mutex_init(tmgrp->mutexp, 0);
191 if (ret != 0)
192 goto err;
195 UNLOCK_TXNREGION(tmgrp);
196 *mgrpp = tmgrp;
197 return (0);
199 err: if (tmgrp->reginfo.addr != NULL) {
200 if (tmgrp->mutexp != NULL)
201 __db_shalloc_free(tmgrp->mem, tmgrp->mutexp);
203 UNLOCK_TXNREGION(tmgrp);
204 (void)__db_rdetach(&tmgrp->reginfo);
205 if (F_ISSET(&tmgrp->reginfo, REGION_CREATED))
206 (void)txn_unlink(path, 1, dbenv);
209 if (tmgrp->reginfo.path != NULL)
210 FREES(tmgrp->reginfo.path);
211 FREE(tmgrp, sizeof(*tmgrp));
212 return (ret);
216 * Internally, we use TXN_DETAIL structures, but we allocate and return
217 * DB_TXN structures that provide access to the transaction ID and the
218 * offset in the transaction region of the TXN_DETAIL structure.
221 txn_begin(tmgrp, parent, txnpp)
222 DB_TXNMGR *tmgrp;
223 DB_TXN *parent;
224 DB_TXN **txnpp;
226 DB_LSN begin_lsn;
227 DB_TXN *retp;
228 TXN_DETAIL *txnp;
229 size_t off;
230 u_int32_t id;
231 int ret;
233 txnp = NULL;
234 *txnpp = NULL;
236 if ((retp = (DB_TXN *)__db_malloc(sizeof(DB_TXN))) == NULL) {
237 __db_err(tmgrp->dbenv, "txn_begin : %s", strerror(ENOMEM));
238 return (ENOMEM);
242 * We do not have to write begin records (and if we do not, then we
243 * need never write records for read-only transactions). However,
244 * we do need to find the current LSN so that we can store it in the
245 * transaction structure, so we can know where to take checkpoints.
247 if (tmgrp->dbenv->lg_info != NULL && (ret =
248 log_put(tmgrp->dbenv->lg_info, &begin_lsn, NULL, DB_CURLSN)) != 0)
249 goto err2;
251 LOCK_TXNREGION(tmgrp);
253 /* Make sure that last_txnid is not going to wrap around. */
254 if (tmgrp->region->last_txnid == TXN_INVALID) {
255 __db_err(tmgrp->dbenv, "txn_begin: %s %s",
256 "Transaction ID wrapping.",
257 "Snapshot your database and start a new log.");
258 ret = EINVAL;
259 goto err1;
262 if ((ret = __txn_validate_region(tmgrp)) != 0)
263 goto err1;
265 /* Allocate a new transaction detail structure. */
266 if ((ret = __db_shalloc(tmgrp->mem, sizeof(TXN_DETAIL), 0, &txnp)) != 0
267 && ret == ENOMEM && (ret = __txn_grow_region(tmgrp)) == 0)
268 ret = __db_shalloc(tmgrp->mem, sizeof(TXN_DETAIL), 0, &txnp);
269 if (ret != 0)
270 goto err1;
272 /* Place transaction on active transaction list. */
273 SH_TAILQ_INSERT_HEAD(&tmgrp->region->active_txn,
274 txnp, links, __txn_detail);
276 id = ++tmgrp->region->last_txnid;
277 tmgrp->region->nbegins++;
279 txnp->txnid = id;
280 txnp->begin_lsn = begin_lsn;
281 ZERO_LSN(txnp->last_lsn);
282 txnp->last_lock = 0;
283 txnp->status = TXN_RUNNING;
284 off = (u_int8_t *)txnp - (u_int8_t *)tmgrp->region;
285 UNLOCK_TXNREGION(tmgrp);
287 ZERO_LSN(retp->last_lsn);
288 retp->txnid = id;
289 retp->parent = parent;
290 retp->mgrp = tmgrp;
291 retp->off = off;
293 LOCK_TXNTHREAD(tmgrp);
294 TAILQ_INSERT_TAIL(&tmgrp->txn_chain, retp, links);
295 UNLOCK_TXNTHREAD(tmgrp);
297 *txnpp = retp;
298 return (0);
300 err1: UNLOCK_TXNREGION(tmgrp);
302 err2: __db_free(retp);
303 return (ret);
307 * txn_commit --
308 * Commit a transaction.
311 txn_commit(txnp)
312 DB_TXN *txnp;
314 DB_LOG *logp;
315 int ret;
317 if ((ret = __txn_check_running(txnp)) != 0)
318 return (ret);
321 * If there are any log records, write a log record and sync
322 * the log, else do no log writes.
324 if ((logp = txnp->mgrp->dbenv->lg_info) != NULL &&
325 !IS_ZERO_LSN(txnp->last_lsn) &&
326 (ret = __txn_regop_log(logp, txnp, &txnp->last_lsn,
327 F_ISSET(txnp->mgrp, DB_TXN_NOSYNC) ? 0 : DB_FLUSH,
328 TXN_COMMIT)) != 0)
329 return (ret);
331 return (__txn_end(txnp, 1));
335 * txn_abort --
336 * Abort a transcation.
339 txn_abort(txnp)
340 DB_TXN *txnp;
342 int ret;
344 if ((ret = __txn_check_running(txnp)) != 0)
345 return (ret);
347 if ((ret = __txn_undo(txnp)) != 0) {
348 __db_err(txnp->mgrp->dbenv,
349 "txn_abort: Log undo failed %s", strerror(ret));
350 return (ret);
352 return (__txn_end(txnp, 0));
356 * Flush the log so a future commit is guaranteed to succeed.
359 txn_prepare(txnp)
360 DB_TXN *txnp;
362 TXN_DETAIL *tp;
363 int ret;
365 if ((ret = __txn_check_running(txnp)) != 0)
366 return (ret);
368 if (txnp->mgrp->dbenv->lg_info != NULL) {
369 if ((ret = log_flush(txnp->mgrp->dbenv->lg_info,
370 &txnp->last_lsn)) != 0)
371 __db_err(txnp->mgrp->dbenv,
372 "txn_prepare: log_flush failed %s\n",
373 strerror(ret));
374 return (ret);
377 LOCK_TXNTHREAD(txnp->mgrp);
378 tp = (TXN_DETAIL *)((u_int8_t *)txnp->mgrp->region + txnp->off);
379 tp->status = TXN_PREPARED;
380 UNLOCK_TXNTHREAD(txnp->mgrp);
381 return (ret);
385 * Return the transaction ID associated with a particular transaction
387 u_int32_t
388 txn_id(txnp)
389 DB_TXN *txnp;
391 return (txnp->txnid);
395 * txn_close --
396 * Close the transaction region, does not imply a checkpoint.
399 txn_close(tmgrp)
400 DB_TXNMGR *tmgrp;
402 DB_TXN *txnp;
403 int ret, t_ret;
405 ret = 0;
408 * This function had better only be called once per process
409 * (i.e., not per thread), so there should be no synchronization
410 * required.
412 while ((txnp =
413 TAILQ_FIRST(&tmgrp->txn_chain)) != TAILQ_END(&tmgrp->txn_chain))
414 if ((t_ret = txn_abort(txnp)) != 0) {
415 __txn_end(txnp, 0);
416 if (ret == 0)
417 ret = t_ret;
420 if (tmgrp->dbenv->lg_info &&
421 (t_ret = log_flush(tmgrp->dbenv->lg_info, NULL)) != 0 && ret == 0)
422 ret = t_ret;
424 if (tmgrp->mutexp != NULL) {
425 LOCK_TXNREGION(tmgrp);
426 __db_shalloc_free(tmgrp->mem, tmgrp->mutexp);
427 UNLOCK_TXNREGION(tmgrp);
430 if ((t_ret = __db_rdetach(&tmgrp->reginfo)) != 0 && ret == 0)
431 ret = t_ret;
433 if (tmgrp->reginfo.path != NULL)
434 FREES(tmgrp->reginfo.path);
435 FREE(tmgrp, sizeof(*tmgrp));
437 return (ret);
441 * txn_unlink --
442 * Remove the transaction region.
445 txn_unlink(path, force, dbenv)
446 const char *path;
447 int force;
448 DB_ENV *dbenv;
450 REGINFO reginfo;
451 int ret;
453 memset(&reginfo, 0, sizeof(reginfo));
454 reginfo.dbenv = dbenv;
455 reginfo.appname = DB_APP_NONE;
456 if (path != NULL && (reginfo.path = (char *)__db_strdup(path)) == NULL)
457 return (ENOMEM);
458 reginfo.file = DEFAULT_TXN_FILE;
459 ret = __db_runlink(&reginfo, force);
460 if (reginfo.path != NULL)
461 FREES(reginfo.path);
462 return (ret);
465 /* Internal routines. */
468 * Return 0 if the txnp is reasonable, otherwise returns EINVAL.
470 static int
471 __txn_check_running(txnp)
472 const DB_TXN *txnp;
474 TXN_DETAIL *tp;
476 tp = NULL;
477 if (txnp != NULL && txnp->mgrp != NULL && txnp->mgrp->region != NULL) {
478 tp = (TXN_DETAIL *)((u_int8_t *)txnp->mgrp->region + txnp->off);
479 if (tp->status != TXN_RUNNING)
480 tp = NULL;
483 return (tp == NULL ? EINVAL : 0);
486 static int
487 __txn_end(txnp, is_commit)
488 DB_TXN *txnp;
489 int is_commit;
491 DB_TXNMGR *mgr;
492 TXN_DETAIL *tp;
493 DB_LOCKREQ request;
494 int ret;
495 u_int32_t locker;
497 mgr = txnp->mgrp;
499 LOCK_TXNTHREAD(mgr);
500 TAILQ_REMOVE(&mgr->txn_chain, txnp, links);
501 UNLOCK_TXNTHREAD(mgr);
503 /* Release the locks. */
504 locker = txnp->txnid;
505 request.op = DB_LOCK_PUT_ALL;
507 if (mgr->dbenv->lk_info) {
508 ret = lock_vec(mgr->dbenv->lk_info, locker, 0,
509 &request, 1, NULL);
510 if (ret != 0 && (ret != DB_LOCK_DEADLOCK || is_commit)) {
511 __db_err(mgr->dbenv, "%s: release locks failed %s",
512 is_commit ? "txn_commit" : "txn_abort",
513 strerror(ret));
514 return (ret);
518 /* End the transaction. */
519 LOCK_TXNREGION(mgr);
520 tp = (TXN_DETAIL *)((u_int8_t *)mgr->region + txnp->off);
521 SH_TAILQ_REMOVE(&mgr->region->active_txn, tp, links, __txn_detail);
522 __db_shalloc_free(mgr->mem, tp);
523 if (is_commit)
524 mgr->region->ncommits++;
525 else
526 mgr->region->naborts++;
527 UNLOCK_TXNREGION(mgr);
529 FREE(txnp, sizeof(*txnp));
531 return (0);
536 * __txn_undo --
537 * Undo the transaction with id txnid. Returns 0 on success and
538 * errno on failure.
540 static int
541 __txn_undo(txnp)
542 DB_TXN *txnp;
544 DBT rdbt;
545 DB_LOG *logp;
546 DB_LSN key_lsn;
547 DB_TXNMGR *mgr;
548 int ret;
550 mgr = txnp->mgrp;
551 logp = mgr->dbenv->lg_info;
552 if (logp == NULL)
553 return (0);
556 * This is the simplest way to code this, but if the mallocs during
557 * recovery turn out to be a performance issue, we can do the
558 * allocation here and use DB_DBT_USERMEM.
560 memset(&rdbt, 0, sizeof(rdbt));
561 if (F_ISSET(logp, DB_AM_THREAD))
562 F_SET(&rdbt, DB_DBT_MALLOC);
564 key_lsn = txnp->last_lsn; /* structure assignment */
565 for (ret = 0; ret == 0 && !IS_ZERO_LSN(key_lsn);) {
567 * The dispatch routine returns the lsn of the record
568 * before the current one in the key_lsn argument.
570 if ((ret = log_get(logp, &key_lsn, &rdbt, DB_SET)) == 0) {
571 ret =
572 mgr->recover(logp, &rdbt, &key_lsn, TXN_UNDO, NULL);
573 if (F_ISSET(logp, DB_AM_THREAD) && rdbt.data != NULL) {
574 __db_free(rdbt.data);
575 rdbt.data = NULL;
578 if (ret != 0)
579 return (ret);
582 return (ret);
586 * Transaction checkpoint.
587 * If either kbytes or minutes is non-zero, then we only take the checkpoint
588 * more than "minutes" minutes have passed since the last checkpoint or if
589 * more than "kbytes" of log data have been written since the last checkpoint.
590 * When taking a checkpoint, find the oldest active transaction and figure out
591 * its first LSN. This is the lowest LSN we can checkpoint, since any record
592 * written after since that point may be involved in a transaction and may
593 * therefore need to be undone in the case of an abort.
596 txn_checkpoint(mgr, kbytes, minutes)
597 const DB_TXNMGR *mgr;
598 u_int32_t kbytes, minutes;
600 TXN_DETAIL *txnp;
601 DB_LSN ckp_lsn, last_ckp;
602 DB_LOG *dblp;
603 u_int32_t kbytes_written;
604 time_t last_ckp_time, now;
605 int ret;
608 * Check if we need to run recovery.
610 ZERO_LSN(ckp_lsn);
611 if (minutes != 0) {
612 (void)time(&now);
614 LOCK_TXNREGION(mgr);
615 last_ckp_time = mgr->region->time_ckp;
616 UNLOCK_TXNREGION(mgr);
618 if (now - last_ckp_time >= (time_t)(minutes * 60))
619 goto do_ckp;
622 if (kbytes != 0) {
623 dblp = mgr->dbenv->lg_info;
624 LOCK_LOGREGION(dblp);
625 kbytes_written =
626 dblp->lp->stat.st_wc_mbytes * 1024 +
627 dblp->lp->stat.st_wc_bytes / 1024;
628 ckp_lsn = dblp->lp->lsn;
629 UNLOCK_LOGREGION(dblp);
630 if (kbytes_written >= (u_int32_t)kbytes)
631 goto do_ckp;
635 * If we checked time and data and didn't go to checkpoint,
636 * we're done.
638 if (minutes != 0 || kbytes != 0)
639 return (0);
641 do_ckp:
642 if (IS_ZERO_LSN(ckp_lsn)) {
643 dblp = mgr->dbenv->lg_info;
644 LOCK_LOGREGION(dblp);
645 ckp_lsn = dblp->lp->lsn;
646 UNLOCK_LOGREGION(dblp);
650 * We have to find an LSN such that all transactions begun
651 * before that LSN are complete.
653 LOCK_TXNREGION(mgr);
655 if (!IS_ZERO_LSN(mgr->region->pending_ckp))
656 ckp_lsn = mgr->region->pending_ckp;
657 else
658 for (txnp =
659 SH_TAILQ_FIRST(&mgr->region->active_txn, __txn_detail);
660 txnp != NULL;
661 txnp = SH_TAILQ_NEXT(txnp, links, __txn_detail)) {
664 * Look through the active transactions for the
665 * lowest begin lsn.
667 if (!IS_ZERO_LSN(txnp->begin_lsn) &&
668 log_compare(&txnp->begin_lsn, &ckp_lsn) < 0)
669 ckp_lsn = txnp->begin_lsn;
672 mgr->region->pending_ckp = ckp_lsn;
673 UNLOCK_TXNREGION(mgr);
675 if (mgr->dbenv->mp_info != NULL &&
676 (ret = memp_sync(mgr->dbenv->mp_info, &ckp_lsn)) != 0) {
678 * ret == DB_INCOMPLETE means that there are still buffers to
679 * flush, the checkpoint is not complete. Wait and try again.
681 if (ret > 0)
682 __db_err(mgr->dbenv,
683 "txn_checkpoint: system failure in memp_sync %s\n",
684 strerror(ret));
685 return (ret);
687 if (mgr->dbenv->lg_info != NULL) {
688 LOCK_TXNREGION(mgr);
689 last_ckp = mgr->region->last_ckp;
690 ZERO_LSN(mgr->region->pending_ckp);
691 UNLOCK_TXNREGION(mgr);
693 if ((ret = __txn_ckp_log(mgr->dbenv->lg_info,
694 NULL, &ckp_lsn, DB_CHECKPOINT, &ckp_lsn, &last_ckp)) != 0) {
695 __db_err(mgr->dbenv,
696 "txn_checkpoint: log failed at LSN [%ld %ld] %s\n",
697 (long)ckp_lsn.file, (long)ckp_lsn.offset,
698 strerror(ret));
699 return (ret);
702 LOCK_TXNREGION(mgr);
703 mgr->region->last_ckp = ckp_lsn;
704 (void)time(&mgr->region->time_ckp);
705 UNLOCK_TXNREGION(mgr);
707 return (0);
711 * __txn_validate_region --
712 * Called at every interface to verify if the region has changed size,
713 * and if so, to remap the region in and reset the process' pointers.
715 static int
716 __txn_validate_region(tp)
717 DB_TXNMGR *tp;
719 int ret;
721 if (tp->reginfo.size == tp->region->hdr.size)
722 return (0);
724 /* Detach/reattach the region. */
725 if ((ret = __db_rreattach(&tp->reginfo, tp->region->hdr.size)) != 0)
726 return (ret);
728 /* Reset region information. */
729 tp->region = tp->reginfo.addr;
730 tp->mem = &tp->region[1];
732 return (0);
735 static int
736 __txn_grow_region(tp)
737 DB_TXNMGR *tp;
739 size_t incr, oldsize;
740 u_int32_t mutex_offset, oldmax;
741 u_int8_t *curaddr;
742 int ret;
744 oldmax = tp->region->maxtxns;
745 incr = oldmax * sizeof(DB_TXN);
746 mutex_offset = tp->mutexp != NULL ?
747 (u_int8_t *)tp->mutexp - (u_int8_t *)tp->region : 0;
749 oldsize = tp->reginfo.size;
750 if ((ret = __db_rgrow(&tp->reginfo, oldsize + incr)) != 0)
751 return (ret);
752 tp->region = tp->reginfo.addr;
754 /* Throw the new space on the free list. */
755 curaddr = (u_int8_t *)tp->region + oldsize;
756 tp->mem = &tp->region[1];
757 tp->mutexp = mutex_offset != 0 ?
758 (db_mutex_t *)((u_int8_t *)tp->region + mutex_offset) : NULL;
760 *((size_t *)curaddr) = incr - sizeof(size_t);
761 curaddr += sizeof(size_t);
762 __db_shalloc_free(tp->mem, curaddr);
764 tp->region->maxtxns = 2 * oldmax;
766 return (0);
770 txn_stat(mgr, statp, db_malloc)
771 DB_TXNMGR *mgr;
772 DB_TXN_STAT **statp;
773 void *(*db_malloc) __P((size_t));
775 DB_TXN_STAT *stats;
776 TXN_DETAIL *txnp;
777 size_t nbytes;
778 u_int32_t nactive, ndx;
780 LOCK_TXNREGION(mgr);
781 nactive = mgr->region->nbegins -
782 mgr->region->naborts - mgr->region->ncommits;
783 UNLOCK_TXNREGION(mgr);
786 * Allocate a bunch of extra active structures to handle any
787 * that have been created since we unlocked the region.
789 nbytes = sizeof(DB_TXN_STAT) + sizeof(DB_TXN_ACTIVE) * (nactive + 200);
790 if (db_malloc == NULL)
791 stats = (DB_TXN_STAT *)__db_malloc(nbytes);
792 else
793 stats = (DB_TXN_STAT *)db_malloc(nbytes);
795 if (stats == NULL)
796 return (ENOMEM);
798 LOCK_TXNREGION(mgr);
799 stats->st_last_txnid = mgr->region->last_txnid;
800 stats->st_last_ckp = mgr->region->last_ckp;
801 stats->st_maxtxns = mgr->region->maxtxns;
802 stats->st_naborts = mgr->region->naborts;
803 stats->st_nbegins = mgr->region->nbegins;
804 stats->st_ncommits = mgr->region->ncommits;
805 stats->st_pending_ckp = mgr->region->pending_ckp;
806 stats->st_time_ckp = mgr->region->time_ckp;
807 stats->st_nactive = stats->st_nbegins -
808 stats->st_naborts - stats->st_ncommits;
809 if (stats->st_nactive > nactive + 200)
810 stats->st_nactive = nactive + 200;
811 stats->st_txnarray = (DB_TXN_ACTIVE *)&stats[1];
813 ndx = 0;
814 for (txnp = SH_TAILQ_FIRST(&mgr->region->active_txn, __txn_detail);
815 txnp != NULL;
816 txnp = SH_TAILQ_NEXT(txnp, links, __txn_detail)) {
817 stats->st_txnarray[ndx].txnid = txnp->txnid;
818 stats->st_txnarray[ndx].lsn = txnp->begin_lsn;
819 ndx++;
821 if (ndx >= stats->st_nactive)
822 break;
825 stats->st_region_wait = mgr->region->hdr.lock.mutex_set_wait;
826 stats->st_region_nowait = mgr->region->hdr.lock.mutex_set_nowait;
827 stats->st_refcnt = mgr->region->hdr.refcnt;
828 stats->st_regsize = mgr->region->hdr.size;
830 UNLOCK_TXNREGION(mgr);
831 *statp = stats;
832 return (0);