2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
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
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
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
46 static const char sccsid
[] = "@(#)txn.c 10.58 (Sleepycat) 5/31/98";
50 #ifndef NO_SYSTEM_INCLUDES
51 #include <sys/types.h>
63 #include "db_dispatch.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.
85 __txn_init(txn_region
)
86 DB_TXNREGION
*txn_region
;
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
));
110 txn_open(path
, flags
, mode
, dbenv
, mgrpp
)
121 /* Validate arguments. */
124 #ifdef HAVE_SPINLOCKS
125 #define OKFLAGS (DB_CREATE | DB_THREAD | DB_TXN_NOSYNC)
127 #define OKFLAGS (DB_CREATE | DB_TXN_NOSYNC)
129 if ((ret
= __db_fchk(dbenv
, "txn_open", flags
, OKFLAGS
)) != 0)
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
));
140 /* Initialize the transaction manager structure. */
141 tmgrp
->mutexp
= NULL
;
142 tmgrp
->dbenv
= dbenv
;
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
;
152 tmgrp
->reginfo
.path
= NULL
;
154 if ((tmgrp
->reginfo
.path
= (char *)__db_strdup(path
)) == NULL
)
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)
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)
175 } else if (tmgrp
->region
->magic
!= DB_TXNMAGIC
) {
176 /* Check if valid region. */
177 __db_err(dbenv
, "txn_open: Bad magic number");
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);
195 UNLOCK_TXNREGION(tmgrp
);
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
));
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
)
236 if ((retp
= (DB_TXN
*)__db_malloc(sizeof(DB_TXN
))) == NULL
) {
237 __db_err(tmgrp
->dbenv
, "txn_begin : %s", strerror(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)
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.");
262 if ((ret
= __txn_validate_region(tmgrp
)) != 0)
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
);
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
++;
280 txnp
->begin_lsn
= begin_lsn
;
281 ZERO_LSN(txnp
->last_lsn
);
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
);
289 retp
->parent
= parent
;
293 LOCK_TXNTHREAD(tmgrp
);
294 TAILQ_INSERT_TAIL(&tmgrp
->txn_chain
, retp
, links
);
295 UNLOCK_TXNTHREAD(tmgrp
);
300 err1
: UNLOCK_TXNREGION(tmgrp
);
302 err2
: __db_free(retp
);
308 * Commit a transaction.
317 if ((ret
= __txn_check_running(txnp
)) != 0)
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
,
331 return (__txn_end(txnp
, 1));
336 * Abort a transcation.
344 if ((ret
= __txn_check_running(txnp
)) != 0)
347 if ((ret
= __txn_undo(txnp
)) != 0) {
348 __db_err(txnp
->mgrp
->dbenv
,
349 "txn_abort: Log undo failed %s", strerror(ret
));
352 return (__txn_end(txnp
, 0));
356 * Flush the log so a future commit is guaranteed to succeed.
365 if ((ret
= __txn_check_running(txnp
)) != 0)
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",
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
);
385 * Return the transaction ID associated with a particular transaction
391 return (txnp
->txnid
);
396 * Close the transaction region, does not imply a checkpoint.
408 * This function had better only be called once per process
409 * (i.e., not per thread), so there should be no synchronization
413 TAILQ_FIRST(&tmgrp
->txn_chain
)) != TAILQ_END(&tmgrp
->txn_chain
))
414 if ((t_ret
= txn_abort(txnp
)) != 0) {
420 if (tmgrp
->dbenv
->lg_info
&&
421 (t_ret
= log_flush(tmgrp
->dbenv
->lg_info
, NULL
)) != 0 && ret
== 0)
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)
433 if (tmgrp
->reginfo
.path
!= NULL
)
434 FREES(tmgrp
->reginfo
.path
);
435 FREE(tmgrp
, sizeof(*tmgrp
));
442 * Remove the transaction region.
445 txn_unlink(path
, force
, dbenv
)
453 memset(®info
, 0, sizeof(reginfo
));
454 reginfo
.dbenv
= dbenv
;
455 reginfo
.appname
= DB_APP_NONE
;
456 if (path
!= NULL
&& (reginfo
.path
= (char *)__db_strdup(path
)) == NULL
)
458 reginfo
.file
= DEFAULT_TXN_FILE
;
459 ret
= __db_runlink(®info
, force
);
460 if (reginfo
.path
!= NULL
)
465 /* Internal routines. */
468 * Return 0 if the txnp is reasonable, otherwise returns EINVAL.
471 __txn_check_running(txnp
)
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
)
483 return (tp
== NULL
? EINVAL
: 0);
487 __txn_end(txnp
, is_commit
)
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,
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",
518 /* End the transaction. */
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
);
524 mgr
->region
->ncommits
++;
526 mgr
->region
->naborts
++;
527 UNLOCK_TXNREGION(mgr
);
529 FREE(txnp
, sizeof(*txnp
));
537 * Undo the transaction with id txnid. Returns 0 on success and
551 logp
= mgr
->dbenv
->lg_info
;
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) {
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
);
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
;
601 DB_LSN ckp_lsn
, last_ckp
;
603 u_int32_t kbytes_written
;
604 time_t last_ckp_time
, now
;
608 * Check if we need to run recovery.
615 last_ckp_time
= mgr
->region
->time_ckp
;
616 UNLOCK_TXNREGION(mgr
);
618 if (now
- last_ckp_time
>= (time_t)(minutes
* 60))
623 dblp
= mgr
->dbenv
->lg_info
;
624 LOCK_LOGREGION(dblp
);
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
)
635 * If we checked time and data and didn't go to checkpoint,
638 if (minutes
!= 0 || kbytes
!= 0)
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.
655 if (!IS_ZERO_LSN(mgr
->region
->pending_ckp
))
656 ckp_lsn
= mgr
->region
->pending_ckp
;
659 SH_TAILQ_FIRST(&mgr
->region
->active_txn
, __txn_detail
);
661 txnp
= SH_TAILQ_NEXT(txnp
, links
, __txn_detail
)) {
664 * Look through the active transactions for the
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.
683 "txn_checkpoint: system failure in memp_sync %s\n",
687 if (mgr
->dbenv
->lg_info
!= NULL
) {
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) {
696 "txn_checkpoint: log failed at LSN [%ld %ld] %s\n",
697 (long)ckp_lsn
.file
, (long)ckp_lsn
.offset
,
703 mgr
->region
->last_ckp
= ckp_lsn
;
704 (void)time(&mgr
->region
->time_ckp
);
705 UNLOCK_TXNREGION(mgr
);
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.
716 __txn_validate_region(tp
)
721 if (tp
->reginfo
.size
== tp
->region
->hdr
.size
)
724 /* Detach/reattach the region. */
725 if ((ret
= __db_rreattach(&tp
->reginfo
, tp
->region
->hdr
.size
)) != 0)
728 /* Reset region information. */
729 tp
->region
= tp
->reginfo
.addr
;
730 tp
->mem
= &tp
->region
[1];
736 __txn_grow_region(tp
)
739 size_t incr
, oldsize
;
740 u_int32_t mutex_offset
, oldmax
;
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)
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
;
770 txn_stat(mgr
, statp
, db_malloc
)
773 void *(*db_malloc
) __P((size_t));
778 u_int32_t nactive
, ndx
;
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
);
793 stats
= (DB_TXN_STAT
*)db_malloc(nbytes
);
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];
814 for (txnp
= SH_TAILQ_FIRST(&mgr
->region
->active_txn
, __txn_detail
);
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
;
821 if (ndx
>= stats
->st_nactive
)
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
);