#define SVN_WANT_BDB
#include "svn_private_config.h"
#include <apr_pools.h>
#include "svn_pools.h"
#include "svn_fs.h"
#include "fs.h"
#include "err.h"
#include "bdb/bdb-err.h"
#include "bdb/bdb_compat.h"
#include "trail.h"
#include "../libsvn_fs/fs-loader.h"
#if defined(SVN_FS__TRAIL_DEBUG)
struct trail_debug_t
{
struct trail_debug_t *prev;
const char *table;
const char *op;
};
void
svn_fs_base__trail_debug(trail_t *trail, const char *table, const char *op)
{
struct trail_debug_t *trail_debug;
trail_debug = apr_palloc(trail->pool, sizeof(*trail_debug));
trail_debug->prev = trail->trail_debug;
trail_debug->table = table;
trail_debug->op = op;
trail->trail_debug = trail_debug;
}
static void
print_trail_debug(trail_t *trail,
const char *txn_body_fn_name,
const char *filename, int line)
{
struct trail_debug_t *trail_debug;
fprintf(stderr, "(%s, %s, %u, %u): ",
txn_body_fn_name, filename, line, trail->db_txn ? 1 : 0);
trail_debug = trail->trail_debug;
while (trail_debug)
{
fprintf(stderr, "(%s, %s) ", trail_debug->table, trail_debug->op);
trail_debug = trail_debug->prev;
}
fprintf(stderr, "\n");
}
#else
#define print_trail_debug(trail, txn_body_fn_name, filename, line)
#endif
static svn_error_t *
begin_trail(trail_t **trail_p,
svn_fs_t *fs,
svn_boolean_t use_txn,
apr_pool_t *pool)
{
base_fs_data_t *bfd = fs->fsap_data;
trail_t *trail = apr_pcalloc(pool, sizeof(*trail));
trail->pool = svn_pool_create(pool);
trail->fs = fs;
if (use_txn)
{
SVN_ERR_ASSERT(! bfd->in_txn_trail);
SVN_ERR(BDB_WRAP(fs, "beginning Berkeley DB transaction",
bfd->bdb->env->txn_begin(bfd->bdb->env, 0,
&trail->db_txn, 0)));
bfd->in_txn_trail = TRUE;
}
else
{
trail->db_txn = NULL;
}
*trail_p = trail;
return SVN_NO_ERROR;
}
static svn_error_t *
abort_trail(trail_t *trail)
{
svn_fs_t *fs = trail->fs;
base_fs_data_t *bfd = fs->fsap_data;
if (trail->db_txn)
{
bfd->in_txn_trail = FALSE;
SVN_ERR(BDB_WRAP(fs, "aborting Berkeley DB transaction",
trail->db_txn->abort(trail->db_txn)));
}
svn_pool_destroy(trail->pool);
return SVN_NO_ERROR;
}
static svn_error_t *
commit_trail(trail_t *trail)
{
int db_err;
svn_fs_t *fs = trail->fs;
base_fs_data_t *bfd = fs->fsap_data;
if (trail->db_txn)
{
bfd->in_txn_trail = FALSE;
SVN_ERR(BDB_WRAP(fs, "committing Berkeley DB transaction",
trail->db_txn->commit(trail->db_txn, 0)));
}
db_err = bfd->bdb->env->txn_checkpoint(bfd->bdb->env, 1024, 5, 0);
if (db_err)
{
#if SVN_BDB_HAS_DB_INCOMPLETE
if (db_err != DB_INCOMPLETE)
#endif
{
return svn_fs_bdb__wrap_db
(fs, "checkpointing after Berkeley DB transaction", db_err);
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
do_retry(svn_fs_t *fs,
svn_error_t *(*txn_body)(void *baton, trail_t *trail),
void *baton,
svn_boolean_t use_txn,
svn_boolean_t destroy_trail_pool,
apr_pool_t *pool,
const char *txn_body_fn_name,
const char *filename,
int line)
{
for (;;)
{
trail_t *trail;
svn_error_t *svn_err, *err;
svn_boolean_t deadlocked = FALSE;
SVN_ERR(begin_trail(&trail, fs, use_txn, pool));
svn_err = (*txn_body)(baton, trail);
if (! svn_err)
{
SVN_ERR(commit_trail(trail));
if (use_txn)
print_trail_debug(trail, txn_body_fn_name, filename, line);
if (destroy_trail_pool)
svn_pool_destroy(trail->pool);
return SVN_NO_ERROR;
}
for (err = svn_err; err; err = err->child)
if (err->apr_err == SVN_ERR_FS_BERKELEY_DB_DEADLOCK)
deadlocked = TRUE;
if (! deadlocked)
{
svn_error_clear(abort_trail(trail));
return svn_err;
}
svn_error_clear(svn_err);
SVN_ERR(abort_trail(trail));
}
}
svn_error_t *
svn_fs_base__retry_debug(svn_fs_t *fs,
svn_error_t *(*txn_body)(void *baton, trail_t *trail),
void *baton,
svn_boolean_t destroy_trail_pool,
apr_pool_t *pool,
const char *txn_body_fn_name,
const char *filename,
int line)
{
return do_retry(fs, txn_body, baton, TRUE, destroy_trail_pool, pool,
txn_body_fn_name, filename, line);
}
#if defined(SVN_FS__TRAIL_DEBUG)
#undef svn_fs_base__retry_txn
#endif
svn_error_t *
svn_fs_base__retry_txn(svn_fs_t *fs,
svn_error_t *(*txn_body)(void *baton, trail_t *trail),
void *baton,
svn_boolean_t destroy_trail_pool,
apr_pool_t *pool)
{
return do_retry(fs, txn_body, baton, TRUE, destroy_trail_pool, pool,
"unknown", "", 0);
}
svn_error_t *
svn_fs_base__retry(svn_fs_t *fs,
svn_error_t *(*txn_body)(void *baton, trail_t *trail),
void *baton,
svn_boolean_t destroy_trail_pool,
apr_pool_t *pool)
{
return do_retry(fs, txn_body, baton, FALSE, destroy_trail_pool, pool,
NULL, NULL, 0);
}