(__collate_table, __collate_extra, __collate_element_hash,
[kopensolaris-gnu/glibc.git] / db2 / txn / txn.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
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.
10  *
11  * This code is derived from software contributed to Berkeley by
12  * Margo Seltzer.
13  *
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.
29  *
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.
41  */
42
43 #include "config.h"
44
45 #ifndef lint
46 static const char sccsid[] = "@(#)txn.c 10.58 (Sleepycat) 5/31/98";
47 #endif /* not lint */
48
49
50 #ifndef NO_SYSTEM_INCLUDES
51 #include <sys/types.h>
52
53 #include <errno.h>
54 #include <string.h>
55 #include <time.h>
56 #endif
57
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"
68
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 *));
75
76 /*
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.
80  *
81  * Initialize a transaction region in shared memory.
82  * Return 0 on success, errno on failure.
83  */
84 static int
85 __txn_init(txn_region)
86         DB_TXNREGION *txn_region;
87 {
88         time_t now;
89
90         (void)time(&now);
91
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));
105
106         return (0);
107 }
108
109 int
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;
116 {
117         DB_TXNMGR *tmgrp;
118         u_int32_t maxtxns;
119         int ret;
120
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);
131
132         maxtxns = dbenv->tx_max != 0 ? dbenv->tx_max : 20;
133
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);
138         }
139
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);
147
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;
165
166         /* Fill in region-related fields. */
167         tmgrp->region = tmgrp->reginfo.addr;
168         tmgrp->mem = &tmgrp->region[1];
169
170         if (F_ISSET(&tmgrp->reginfo, REGION_CREATED)) {
171                 tmgrp->region->maxtxns = maxtxns;
172                 if ((ret = __txn_init(tmgrp->region)) != 0)
173                         goto err;
174
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;
180         }
181
182         if (LF_ISSET(DB_THREAD)) {
183                 if ((ret = __db_shalloc(tmgrp->mem, sizeof(db_mutex_t),
184                     MUTEX_ALIGNMENT, &tmgrp->mutexp)) == 0)
185                         /*
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.
189                          */
190                         __db_mutex_init(tmgrp->mutexp, 0);
191                 if (ret != 0)
192                         goto err;
193         }
194
195         UNLOCK_TXNREGION(tmgrp);
196         *mgrpp = tmgrp;
197         return (0);
198
199 err:    if (tmgrp->reginfo.addr != NULL) {
200                 if (tmgrp->mutexp != NULL)
201                         __db_shalloc_free(tmgrp->mem, tmgrp->mutexp);
202
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);
207         }
208
209         if (tmgrp->reginfo.path != NULL)
210                 FREES(tmgrp->reginfo.path);
211         FREE(tmgrp, sizeof(*tmgrp));
212         return (ret);
213 }
214
215 /*
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.
219  */
220 int
221 txn_begin(tmgrp, parent, txnpp)
222         DB_TXNMGR *tmgrp;
223         DB_TXN *parent;
224         DB_TXN **txnpp;
225 {
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;
232
233         txnp = NULL;
234         *txnpp = NULL;
235
236         if ((retp = (DB_TXN *)__db_malloc(sizeof(DB_TXN))) == NULL) {
237                 __db_err(tmgrp->dbenv, "txn_begin : %s", strerror(ENOMEM));
238                 return (ENOMEM);
239         }
240
241         /*
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.
246          */
247         if (tmgrp->dbenv->lg_info != NULL && (ret =
248             log_put(tmgrp->dbenv->lg_info, &begin_lsn, NULL, DB_CURLSN)) != 0)
249                 goto err2;
250
251         LOCK_TXNREGION(tmgrp);
252
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;
260         }
261
262         if ((ret = __txn_validate_region(tmgrp)) != 0)
263                 goto err1;
264
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;
271
272         /* Place transaction on active transaction list. */
273         SH_TAILQ_INSERT_HEAD(&tmgrp->region->active_txn,
274             txnp, links, __txn_detail);
275
276         id = ++tmgrp->region->last_txnid;
277         tmgrp->region->nbegins++;
278
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);
286
287         ZERO_LSN(retp->last_lsn);
288         retp->txnid = id;
289         retp->parent = parent;
290         retp->mgrp = tmgrp;
291         retp->off = off;
292
293         LOCK_TXNTHREAD(tmgrp);
294         TAILQ_INSERT_TAIL(&tmgrp->txn_chain, retp, links);
295         UNLOCK_TXNTHREAD(tmgrp);
296
297         *txnpp = retp;
298         return (0);
299
300 err1:   UNLOCK_TXNREGION(tmgrp);
301
302 err2:   __db_free(retp);
303         return (ret);
304 }
305
306 /*
307  * txn_commit --
308  *      Commit a transaction.
309  */
310 int
311 txn_commit(txnp)
312         DB_TXN *txnp;
313 {
314         DB_LOG *logp;
315         int ret;
316
317         if ((ret = __txn_check_running(txnp)) != 0)
318                 return (ret);
319
320         /*
321          * If there are any log records, write a log record and sync
322          * the log, else do no log writes.
323          */
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);
330
331         return (__txn_end(txnp, 1));
332 }
333
334 /*
335  * txn_abort --
336  *      Abort a transcation.
337  */
338 int
339 txn_abort(txnp)
340         DB_TXN *txnp;
341 {
342         int ret;
343
344         if ((ret = __txn_check_running(txnp)) != 0)
345                 return (ret);
346
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);
351         }
352         return (__txn_end(txnp, 0));
353 }
354
355 /*
356  * Flush the log so a future commit is guaranteed to succeed.
357  */
358 int
359 txn_prepare(txnp)
360         DB_TXN *txnp;
361 {
362         TXN_DETAIL *tp;
363         int ret;
364
365         if ((ret = __txn_check_running(txnp)) != 0)
366                 return (ret);
367
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);
375         }
376
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);
382 }
383
384 /*
385  * Return the transaction ID associated with a particular transaction
386  */
387 u_int32_t
388 txn_id(txnp)
389         DB_TXN *txnp;
390 {
391         return (txnp->txnid);
392 }
393
394 /*
395  * txn_close --
396  *      Close the transaction region, does not imply a checkpoint.
397  */
398 int
399 txn_close(tmgrp)
400         DB_TXNMGR *tmgrp;
401 {
402         DB_TXN *txnp;
403         int ret, t_ret;
404
405         ret = 0;
406
407         /*
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.
411          */
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;
418                 }
419
420         if (tmgrp->dbenv->lg_info &&
421             (t_ret = log_flush(tmgrp->dbenv->lg_info, NULL)) != 0 && ret == 0)
422                 ret = t_ret;
423
424         if (tmgrp->mutexp != NULL) {
425                 LOCK_TXNREGION(tmgrp);
426                 __db_shalloc_free(tmgrp->mem, tmgrp->mutexp);
427                 UNLOCK_TXNREGION(tmgrp);
428         }
429
430         if ((t_ret = __db_rdetach(&tmgrp->reginfo)) != 0 && ret == 0)
431                 ret = t_ret;
432
433         if (tmgrp->reginfo.path != NULL)
434                 FREES(tmgrp->reginfo.path);
435         FREE(tmgrp, sizeof(*tmgrp));
436
437         return (ret);
438 }
439
440 /*
441  * txn_unlink --
442  *      Remove the transaction region.
443  */
444 int
445 txn_unlink(path, force, dbenv)
446         const char *path;
447         int force;
448         DB_ENV *dbenv;
449 {
450         REGINFO reginfo;
451         int ret;
452
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);
463 }
464
465 /* Internal routines. */
466
467 /*
468  * Return 0 if the txnp is reasonable, otherwise returns EINVAL.
469  */
470 static int
471 __txn_check_running(txnp)
472         const DB_TXN *txnp;
473 {
474         TXN_DETAIL *tp;
475
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;
481         }
482
483         return (tp == NULL ? EINVAL : 0);
484 }
485
486 static int
487 __txn_end(txnp, is_commit)
488         DB_TXN *txnp;
489         int is_commit;
490 {
491         DB_TXNMGR *mgr;
492         TXN_DETAIL *tp;
493         DB_LOCKREQ request;
494         int ret;
495         u_int32_t locker;
496
497         mgr = txnp->mgrp;
498
499         LOCK_TXNTHREAD(mgr);
500         TAILQ_REMOVE(&mgr->txn_chain, txnp, links);
501         UNLOCK_TXNTHREAD(mgr);
502
503         /* Release the locks. */
504         locker = txnp->txnid;
505         request.op = DB_LOCK_PUT_ALL;
506
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);
515                 }
516         }
517
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);
528
529         FREE(txnp, sizeof(*txnp));
530
531         return (0);
532 }
533
534
535 /*
536  * __txn_undo --
537  *      Undo the transaction with id txnid.  Returns 0 on success and
538  *      errno on failure.
539  */
540 static int
541 __txn_undo(txnp)
542         DB_TXN *txnp;
543 {
544         DBT rdbt;
545         DB_LOG *logp;
546         DB_LSN key_lsn;
547         DB_TXNMGR *mgr;
548         int ret;
549
550         mgr = txnp->mgrp;
551         logp = mgr->dbenv->lg_info;
552         if (logp == NULL)
553                 return (0);
554
555         /*
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.
559          */
560         memset(&rdbt, 0, sizeof(rdbt));
561         if (F_ISSET(logp, DB_AM_THREAD))
562                 F_SET(&rdbt, DB_DBT_MALLOC);
563
564         key_lsn = txnp->last_lsn;               /* structure assignment */
565         for (ret = 0; ret == 0 && !IS_ZERO_LSN(key_lsn);) {
566                 /*
567                  * The dispatch routine returns the lsn of the record
568                  * before the current one in the key_lsn argument.
569                  */
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;
576                         }
577                 }
578                 if (ret != 0)
579                         return (ret);
580         }
581
582         return (ret);
583 }
584
585 /*
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.
594  */
595 int
596 txn_checkpoint(mgr, kbytes, minutes)
597         const DB_TXNMGR *mgr;
598         u_int32_t kbytes, minutes;
599 {
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;
606
607         /*
608          * Check if we need to run recovery.
609          */
610         ZERO_LSN(ckp_lsn);
611         if (minutes != 0) {
612                 (void)time(&now);
613
614                 LOCK_TXNREGION(mgr);
615                 last_ckp_time = mgr->region->time_ckp;
616                 UNLOCK_TXNREGION(mgr);
617
618                 if (now - last_ckp_time >= (time_t)(minutes * 60))
619                         goto do_ckp;
620         }
621
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;
632         }
633
634         /*
635          * If we checked time and data and didn't go to checkpoint,
636          * we're done.
637          */
638         if (minutes != 0 || kbytes != 0)
639                 return (0);
640
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);
647         }
648
649         /*
650          * We have to find an LSN such that all transactions begun
651          * before that LSN are complete.
652          */
653         LOCK_TXNREGION(mgr);
654
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)) {
662
663                         /*
664                          * Look through the active transactions for the
665                          * lowest begin lsn.
666                          */
667                         if (!IS_ZERO_LSN(txnp->begin_lsn) &&
668                             log_compare(&txnp->begin_lsn, &ckp_lsn) < 0)
669                                 ckp_lsn = txnp->begin_lsn;
670                 }
671
672         mgr->region->pending_ckp = ckp_lsn;
673         UNLOCK_TXNREGION(mgr);
674
675         if (mgr->dbenv->mp_info != NULL &&
676             (ret = memp_sync(mgr->dbenv->mp_info, &ckp_lsn)) != 0) {
677                 /*
678                  * ret == DB_INCOMPLETE means that there are still buffers to
679                  * flush, the checkpoint is not complete.  Wait and try again.
680                  */
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);
686         }
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);
692
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);
700                 }
701
702                 LOCK_TXNREGION(mgr);
703                 mgr->region->last_ckp = ckp_lsn;
704                 (void)time(&mgr->region->time_ckp);
705                 UNLOCK_TXNREGION(mgr);
706         }
707         return (0);
708 }
709
710 /*
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.
714  */
715 static int
716 __txn_validate_region(tp)
717         DB_TXNMGR *tp;
718 {
719         int ret;
720
721         if (tp->reginfo.size == tp->region->hdr.size)
722                 return (0);
723
724         /* Detach/reattach the region. */
725         if ((ret = __db_rreattach(&tp->reginfo, tp->region->hdr.size)) != 0)
726                 return (ret);
727
728         /* Reset region information. */
729         tp->region = tp->reginfo.addr;
730         tp->mem = &tp->region[1];
731
732         return (0);
733 }
734
735 static int
736 __txn_grow_region(tp)
737         DB_TXNMGR *tp;
738 {
739         size_t incr, oldsize;
740         u_int32_t mutex_offset, oldmax;
741         u_int8_t *curaddr;
742         int ret;
743
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;
748
749         oldsize = tp->reginfo.size;
750         if ((ret = __db_rgrow(&tp->reginfo, oldsize + incr)) != 0)
751                 return (ret);
752         tp->region = tp->reginfo.addr;
753
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;
759
760         *((size_t *)curaddr) = incr - sizeof(size_t);
761         curaddr += sizeof(size_t);
762         __db_shalloc_free(tp->mem, curaddr);
763
764         tp->region->maxtxns = 2 * oldmax;
765
766         return (0);
767 }
768
769 int
770 txn_stat(mgr, statp, db_malloc)
771         DB_TXNMGR *mgr;
772         DB_TXN_STAT **statp;
773         void *(*db_malloc) __P((size_t));
774 {
775         DB_TXN_STAT *stats;
776         TXN_DETAIL *txnp;
777         size_t nbytes;
778         u_int32_t nactive, ndx;
779
780         LOCK_TXNREGION(mgr);
781         nactive = mgr->region->nbegins -
782             mgr->region->naborts - mgr->region->ncommits;
783         UNLOCK_TXNREGION(mgr);
784
785         /*
786          * Allocate a bunch of extra active structures to handle any
787          * that have been created since we unlocked the region.
788          */
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);
794
795         if (stats == NULL)
796                 return (ENOMEM);
797
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];
812
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++;
820
821                 if (ndx >= stats->st_nactive)
822                         break;
823         }
824
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;
829
830         UNLOCK_TXNREGION(mgr);
831         *statp = stats;
832         return (0);
833 }