/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996, 1997, 1998
* Sleepycat Software. All rights reserved.
*/
/*
* Copyright (c) 1995, 1996
* The President and Fellows of Harvard University. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Margo Seltzer.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#ifndef lint
#endif /* not lint */
#ifndef NO_SYSTEM_INCLUDES
#include <errno.h>
#include <string.h>
#include <time.h>
#endif
#include "db_int.h"
#include "shqueue.h"
#include "db_page.h"
#include "db_shash.h"
#include "txn.h"
#include "db_dispatch.h"
#include "lock.h"
#include "log.h"
#include "db_am.h"
#include "common_ext.h"
/*
* This file contains the top level routines of the transaction library.
* It assumes that a lock manager and log manager that conform to the db_log(3)
* and db_lock(3) interfaces exist.
*
* Initialize a transaction region in shared memory.
* Return 0 on success, errno on failure.
*/
static int
{
/* maxtxns is already initialized. */
/*
* XXX
* If we ever do more types of locking and logging, this changes.
*/
txn_region->logtype = 0;
txn_region->locktype = 0;
return (0);
}
int
const char *path;
int mode;
{
int ret;
/* Validate arguments. */
return (EINVAL);
#ifdef HAVE_SPINLOCKS
#else
#endif
return (ret);
/* Now, create the transaction manager structure and set its fields. */
return (ret);
/* Initialize the transaction manager structure. */
else
goto err;
goto err;
/* Fill in region-related fields. */
goto err;
/* Check if valid region. */
goto err;
}
/*
* Since we only get here if threading is turned on, we
* know that we have spinlocks, so the offset is going
* to be ignored. We put 0 here as a valid placeholder.
*/
if (ret != 0)
goto err;
}
return (0);
}
return (ret);
}
/*
* __txn_panic --
* Panic a transaction region.
*
* PUBLIC: void __txn_panic __P((DB_ENV *));
*/
void
{
}
/*
* txn_begin --
* This is a wrapper to the actual begin process. Normal txn_begin()
* allocates a DB_TXN structure for the caller, while txn_xa_begin() does
* not. Other than that, both call into the common __txn_begin code().
*
* Internally, we use TXN_DETAIL structures, but the DB_TXN structure
* provides access to the transaction ID and the offset in the transaction
* region of the TXN_DETAIL structure.
*/
int
{
int ret;
return (ret);
}
return (ret);
}
/*
* __txn_xa_begin --
* XA version of txn_begin.
*
* PUBLIC: int __txn_xa_begin __P((DB_ENV *, DB_TXN *));
*/
int
{
return (__txn_begin(txn));
}
/*
* __txn_begin --
* Normal DB version of txn_begin.
*/
static int
{
int ret;
/*
* We do not have to write begin records (and if we do not, then we
* need never write records for read-only transactions). However,
* we do need to find the current LSN so that we can store it in the
* transaction structure, so we can know where to take checkpoints.
*/
goto err2;
/* Make sure that last_txnid is not going to wrap around. */
"Transaction ID wrapping.",
"Snapshot your database and start a new log.");
goto err1;
}
goto err1;
/* Allocate a new transaction detail structure. */
if (ret != 0)
goto err1;
/* Place transaction on active transaction list. */
else
}
return (0);
}
/*
* txn_commit --
* Commit a transaction.
*/
int
{
int ret;
return (ret);
/*
* If there are any log records, write a log record and sync
* the log, else do no log writes. If the commit is for a child
* transaction, we do not need to commit the child synchronously
* since if its parent aborts, it will abort too and its parent
* (or ultimate ancestor) will write synchronously.
*/
else
if (ret != 0)
return (ret);
}
/*
* If this is the senior ancestor (i.e., it has no children), then we
* can release all the child transactions since everyone is committing.
* Then we can release this transaction. If this is not the ultimate
* ancestor, then we can neither free it or its children.
*/
}
/*
* txn_abort --
* Abort a transcation.
*/
int
{
int ret;
return (ret);
return (ret);
}
}
/*
* txn_prepare --
* Flush the log so a future commit is guaranteed to succeed.
*/
int
{
int ret;
return (ret);
/*
* We indicate that a transaction is an XA transaction by putting
* a valid size in the xid.size fiels. XA requires that the transaction
* be either ENDED or SUSPENDED when prepare is called, so we know
* that if the xa_status isn't in one of those states, but we are
* calling prepare that we are not an XA transaction.
*/
return (ret);
}
return (ret);
}
/*
* Return the transaction ID associated with a particular transaction
*/
{
}
/*
* txn_close --
* Close the transaction region, does not imply a checkpoint.
*/
int
{
ret = 0;
/*
* This function had better only be called once per process
* (i.e., not per thread), so there should be no synchronization
* required.
*/
while ((txnp =
if (ret == 0)
}
}
return (ret);
}
/*
* txn_unlink --
* Remove the transaction region.
*/
int
const char *path;
int force;
{
int ret;
return (ret);
return (ret);
}
/* Internal routines. */
/*
* Return 0 if the txnp is reasonable, otherwise returns EINVAL.
*/
static int
TXN_DETAIL **tdp;
{
/*
* Child transactions could be marked committed which is OK.
*/
}
}
static int
int is_commit;
{
int ret;
/* Release the locks. */
ret =
return (ret);
}
}
/* End the transaction. */
/*
* Child transactions that are committing cannot be released until
* the parent commits, since the parent may abort, causing the child
* to abort as well.
*/
} else
if (is_commit)
else
/*
* If the transaction aborted, we can remove it from its parent links.
* If it committed, then we need to leave it on, since the parent can
* still abort.
*/
/* Free the space. */
}
return (0);
}
/*
* __txn_undo --
* Undo the transaction with id txnid. Returns 0 on success and
* errno on failure.
*/
static int
{
int ret;
return (0);
/*
* This is the simplest way to code this, but if the mallocs during
* recovery turn out to be a performance issue, we can do the
* allocation here and use DB_DBT_USERMEM.
*/
/*
* The dispatch routine returns the lsn of the record
* before the current one in the key_lsn argument.
*/
ret =
}
}
if (ret != 0)
return (ret);
}
return (ret);
}
/*
* Transaction checkpoint.
* If either kbytes or minutes is non-zero, then we only take the checkpoint
* more than "minutes" minutes have passed since the last checkpoint or if
* more than "kbytes" of log data have been written since the last checkpoint.
* When taking a checkpoint, find the oldest active transaction and figure out
* its first LSN. This is the lowest LSN we can checkpoint, since any record
* written after since that point may be involved in a transaction and may
* therefore need to be undone in the case of an abort.
*/
int
{
int ret;
/*
* Check if we need to run recovery.
*/
if (minutes != 0) {
goto do_ckp;
}
if (kbytes != 0) {
goto do_ckp;
}
/*
* If we checked time and data and didn't go to checkpoint,
* we're done.
*/
return (0);
if (IS_ZERO_LSN(ckp_lsn)) {
}
/*
* We have to find an LSN such that all transactions begun
* before that LSN are complete.
*/
else
for (txnp =
/*
* Look through the active transactions for the
* lowest begin lsn.
*/
}
/*
* memp_sync may change the lsn you pass it, so don't pass it
* the actual ckp_lsn, pass it a temp instead.
*/
/*
* ret == DB_INCOMPLETE means that there are still buffers to
* flush, the checkpoint is not complete. Wait and try again.
*/
if (ret > 0)
"txn_checkpoint: system failure in memp_sync %s\n",
return (ret);
}
"txn_checkpoint: log failed at LSN [%ld %ld] %s\n",
return (ret);
}
}
return (0);
}
/*
* __txn_validate_region --
* Called at every interface to verify if the region has changed size,
* and if so, to remap the region in and reset the process' pointers.
*/
static int
{
int ret;
return (0);
return (ret);
/* Reset region information. */
return (0);
}
static int
{
int ret;
return (ret);
/* Throw the new space on the free list. */
return (0);
}
int
DB_TXN_STAT **statp;
{
int ret;
/*
* Allocate a bunch of extra active structures to handle any
* that have been created since we unlocked the region.
*/
return (ret);
ndx = 0;
ndx++;
break;
}
return (0);
}
static void
{
/* Free any children of this transaction. */
/* Free the transaction detail in the region. */
/* Now remove from its parent. */
}
}
}
/*
* __txn_is_ancestor --
* Determine if a transaction is an ancestor of another transaction.
* This is used during lock promotion when we do not have the per-process
* data structures that link parents together. Instead, we'll have to
* follow the links in the transaction region.
*
* PUBLIC: int __txn_is_ancestor __P((DB_TXNMGR *, size_t, size_t));
*/
int
{
req_tp =
return (1);
}
return (0);
}