libdb/lang/cxx/stl/dbstl_resource_manager.cpp

1069 lines
26 KiB
C++

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2009, 2011 Oracle and/or its affiliates. All rights reserved.
*
* $Id$
*/
#include <assert.h>
#include <utility>
#include "dbstl_resource_manager.h"
#include "dbstl_exception.h"
#include "dbstl_dbc.h"
START_NS(dbstl)
typedef struct {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
} db_timespec;
extern "C"{
void __os_id (DB_ENV *, pid_t *, db_threadid_t*);
void __os_gettime(ENV *env, db_timespec *tp, int monotonic);
}
using std::pair;
using std::make_pair;
// Static data member definitions.
map<Db*, size_t> ResourceManager::open_dbs_;
map<DbEnv*, size_t> ResourceManager::open_envs_;
set<DbstlGlobalInnerObject *> ResourceManager::glob_objs_;
set<Db *> ResourceManager::deldbs;
set<DbEnv *> ResourceManager::delenvs;
DbEnv * ResourceManager::mtx_env_ = NULL;
db_mutex_t ResourceManager::mtx_handle_ = 0;
db_mutex_t ResourceManager::mtx_globj_ = 0;
#ifdef TLS_DEFN_MODIFIER
template <Typename T> TLS_DEFN_MODIFIER T *TlsWrapper<T>::tinst_ = NULL;
#elif defined(HAVE_PTHREAD_TLS)
static pthread_once_t once_control_ = PTHREAD_ONCE_INIT;
template <Typename T>
pthread_key_t TlsWrapper<T>::tls_key_;
template<Typename T>
void tls_init_once(void) {
pthread_key_create(&TlsWrapper<T>::tls_key_, NULL);
}
template<Typename T>
TlsWrapper<T>::TlsWrapper()
{
pthread_once(&once_control_, tls_init_once<T>);
}
#else
#error "No suitable thread-local storage model configured"
#endif
int ResourceManager::global_lock(db_mutex_t dbcontainer_mtx)
{
int ret;
ret = mtx_env_->mutex_lock(dbcontainer_mtx);
dbstl_assert(ret == 0);
return 0;
}
int ResourceManager::global_unlock(db_mutex_t dbcontainer_mtx)
{
int ret;
ret = mtx_env_->mutex_unlock(dbcontainer_mtx);
dbstl_assert(ret == 0);
return 0;
}
u_int32_t dbstl_strlen(const char *str)
{
return (u_int32_t)strlen(str);
}
void dbstl_strcpy(char *dest, const char *src, size_t num)
{
strncpy(dest, src, num);
}
int dbstl_strncmp(const char *s1, const char *s2, size_t num)
{
return strncmp(s1, s2, num);
}
int dbstl_wcsncmp(const wchar_t *s1, const wchar_t *s2, size_t num)
{
return wcsncmp(s1, s2, num);
}
int dbstl_strcmp(const char *s1, const char *s2)
{
return strcmp(s1, s2);
}
int dbstl_wcscmp(const wchar_t *s1, const wchar_t *s2)
{
return wcscmp(s1, s2);
}
u_int32_t dbstl_wcslen(const wchar_t *str)
{
return (u_int32_t)wcslen(str);
}
void dbstl_wcscpy(wchar_t *dest, const wchar_t *src, size_t num)
{
wcsncpy(dest, src, num);
}
// This function should be called in a single thread inside a process, before
// any use of dbstl. We don't want to rely on platform dependent mutex API,
// so we defer the synchronization to users.
void ResourceManager::global_startup()
{
int ret;
db_timespec tnow;
if (mtx_env_ == NULL) {
mtx_env_ = new DbEnv(DB_CXX_NO_EXCEPTIONS);
// Set cache size to 32k, to save space.
BDBOP(mtx_env_->set_cachesize(0, 32 * 1024, 1), ret);
BDBOP(mtx_env_->mutex_set_max(DBSTL_MAX_MTX_ENV_MUTEX), ret);
BDBOP2(mtx_env_->open(NULL, DB_PRIVATE | DB_CREATE, 0777),
ret, mtx_env_->close(0));
BDBOP2(mtx_env_->mutex_alloc(DB_MUTEX_PROCESS_ONLY,
&mtx_handle_), ret, mtx_env_->mutex_free(mtx_handle_));
BDBOP2(mtx_env_->mutex_alloc(DB_MUTEX_PROCESS_ONLY,
&mtx_globj_), ret, mtx_env_->mutex_free(mtx_globj_));
__os_gettime(NULL, &tnow, 0);
srand((unsigned int)tnow.tv_sec);
}
}
ResourceManager::ResourceManager(void)
{
// Initialize process wide dbstl settings. If there are multiple
// threads, the global_startup should be called in a single thread
// before any use of dbstl.
global_startup();
}
void ResourceManager::close_db(Db *pdb)
{
bool berase = false;
if (pdb == NULL)
return;
db_csr_map_t::iterator itr = all_csrs_.find(pdb);
if (itr == all_csrs_.end())
return;
this->close_db_cursors(pdb);
delete all_csrs_[pdb];
all_csrs_.erase(itr);
pdb->close(0);
set<Db *>::iterator itrdb = deldbs.find(pdb);
// If new'ed by open_db, delete it.
if (itrdb != deldbs.end()) {
delete *itrdb;
berase = true;
}
global_lock(mtx_handle_);
open_dbs_.erase(pdb);
if (berase)
deldbs.erase(itrdb);
global_unlock(mtx_handle_);
}
void ResourceManager::close_all_db_envs()
{
u_int32_t oflags;
int ret;
size_t txnstk_sz;
global_lock(mtx_handle_);
for (map<DbEnv*, size_t>::iterator i = open_envs_.begin();
i != open_envs_.end(); ++i) {
BDBOP(i->first->get_open_flags(&oflags), ret);
txnstk_sz = env_txns_[i->first].size();
if (oflags & DB_INIT_CDB) {
assert(txnstk_sz == 1);
BDBOP(env_txns_[i->first].top()->commit(0), ret);
} else
assert(txnstk_sz == 0);
i->first->close(0);
}
// Delete DbEnv objects new'ed by dbstl.
set<DbEnv *>::iterator itr2 = delenvs.begin();
for (; itr2 != delenvs.end(); ++itr2)
delete *itr2;
delenvs.clear();
env_txns_.clear();
open_envs_.clear();
global_unlock(mtx_handle_);
}
void ResourceManager::close_db_env(DbEnv *penv)
{
u_int32_t oflags;
int ret;
size_t txnstk_sz;
bool berase = false;
if (penv == NULL)
return;
map<DbEnv *, txnstk_t>::iterator itr = env_txns_.find(penv);
if (itr == env_txns_.end())
return;
BDBOP(penv->get_open_flags(&oflags), ret);
txnstk_sz = itr->second.size();
if (oflags & DB_INIT_CDB) {
assert(txnstk_sz == 1);
BDBOP(itr->second.top()->commit(0), ret);
} else
assert(txnstk_sz == 0);
env_txns_.erase(itr);
penv->close(0);
set<DbEnv *>::iterator itrdb = delenvs.find(penv);
// If new'ed by open_db, delete it.
if (itrdb != delenvs.end()) {
delete penv;
berase = true;
}
global_lock(mtx_handle_);
open_envs_.erase(penv);
if (berase)
delenvs.erase(itrdb);
global_unlock(mtx_handle_);
}
void ResourceManager::close_all_dbs()
{
map<Db *, size_t>::iterator itr;
set<Db *>::iterator itr2;
Db *pdb;
global_lock(mtx_handle_);
for (itr = open_dbs_.begin(); itr != open_dbs_.end(); ++itr) {
pdb = itr->first;
this->close_db_cursors(pdb);
delete all_csrs_[pdb];
all_csrs_.erase(pdb);
pdb->close(0);
}
// Delete Db objects new'ed by dbstl.
for (itr2 = deldbs.begin(); itr2 != deldbs.end(); ++itr2)
delete *itr2;
deldbs.clear();
open_dbs_.clear();
global_unlock(mtx_handle_);
}
ResourceManager::~ResourceManager(void)
{
u_int32_t oflags;
int ret;
set<Db *> dbs2del;
set<DbEnv *> envs2del;
global_lock(mtx_handle_);
for (map<Db*, size_t>::iterator i = open_dbs_.begin();
i != open_dbs_.end(); ++i) {
this->close_db_cursors(i->first);
(i->second)--;
if (i->second == 0) {
delete all_csrs_[i->first]; // Delete the cursor set.
all_csrs_.erase(i->first);
i->first->close(0);
set<Db *>::iterator itrdb = deldbs.find(i->first);
// If new'ed by open_db, delete it.
if (itrdb != deldbs.end()) {
delete *itrdb;
deldbs.erase(itrdb);
}
dbs2del.insert(i->first);
}
}
for (map<DbEnv*, size_t>::iterator i = open_envs_.begin();
i != open_envs_.end(); ++i) {
BDBOP(i->first->get_open_flags(&oflags), ret);
if (oflags & DB_INIT_CDB) {
assert(env_txns_[i->first].size() == 1);
BDBOP(env_txns_[i->first].top()->commit(0), ret);
env_txns_[i->first].pop();
}
(i->second)--;
if (i->second == 0) {
assert(env_txns_[i->first].size() == 0);
i->first->close(0);
set<DbEnv *>::iterator itrdb = delenvs.find(i->first);
// If new'ed by open_db, delete it.
if (itrdb != delenvs.end()) {
delete *itrdb;
delenvs.erase(itrdb);
}
envs2del.insert(i->first);
}
}
// Erase db/env entries that are just closed.
for (set<Db *>::iterator i = dbs2del.begin(); i != dbs2del.end(); ++i)
open_dbs_.erase(*i);
for (set<DbEnv *>::iterator i = envs2del.begin();
i != envs2del.end(); ++i)
open_envs_.erase(*i);
global_unlock(mtx_handle_);
for (db_csr_map_t::iterator itr3 = all_csrs_.begin();
itr3 != all_csrs_.end(); ++itr3)
{
// Delete the cursor set. Above code may not have a chance to
// delete this set because the db(itr3->first) was already
// closed by another thread.
delete itr3->second;
}
// Don't bother to clear all_csrs_ since it is being destructed.
// Don't handle transactions, leave them alone, because autocommit
// transactions must have been committed/aborted, and outside transactions
// should be handled by user code, and if they are not handled yet,
// the DbEnv::close will fail.
// Only handle the transaction for CDS mode---an DB_TXN* handle is opened
// at environment registration/creation by cdsgroup_begin, so we need to commit
// that transaction.
//
}
Db* ResourceManager::open_db (
DbEnv*penv, const char* filename, DBTYPE dbtype,
u_int32_t oflags, u_int32_t set_flags1, int mode, DbTxn* txn,
u_int32_t cflags, const char* dbname)
{
int ret, ci = 0;
u_int32_t envf = 0, envoflags = 0;
DbTxn *ptxn = NULL;
Db *pdb = new Db(penv, cflags | DB_CXX_NO_EXCEPTIONS);
if (penv) {
BDBOP(penv->get_open_flags(&envoflags), ret);
BDBOP(penv->get_flags(&envf), ret);
}
if (set_flags1)
BDBOP(pdb->set_flags(set_flags1), ret);
// If no transaction is specified and we really need one, begin a
// transaction and commit it before return, we don't commit
// passed-in transaction.
//
if (penv && ((envf & DB_AUTO_COMMIT) ||
(envoflags & DB_INIT_TXN)) && txn == 0){
ptxn = current_txn(penv);
BDBOP2(penv->txn_begin(ptxn, &txn, 0), ret, txn->abort());
ci = 1;
}
if (txn == NULL)
BDBOP2(pdb->open(txn, filename, dbname,
dbtype, oflags, mode),
ret, (pdb->close(0)));
else
BDBOP2(pdb->open(txn, filename, dbname,
dbtype, oflags, mode),
ret, (pdb->close(0), txn->abort()));
if (ci && txn)
BDBOP(txn->commit(0), ret);
global_lock(mtx_handle_);
open_dbs_.insert(make_pair(pdb, 1u));
pair<set<Db *>::iterator, bool> delinsret = deldbs.insert(pdb);
assert(delinsret.second);
global_unlock(mtx_handle_);
csrset_t *mycsrs = new csrset_t();
all_csrs_.insert(make_pair(pdb, mycsrs));
return pdb;
}
// Only called if the user does not supply an environment handle.
DbEnv* ResourceManager::open_env(const char* env_home, u_int32_t set_flags1,
u_int32_t oflags, u_int32_t cachesize, int mode, u_int32_t cflags)
{
int ret;
DbEnv *penv = new DbEnv(cflags | DB_CXX_NO_EXCEPTIONS);
if (set_flags1)
BDBOP(penv->set_flags(set_flags1, 1), ret);
BDBOP(penv->set_cachesize(0, cachesize, 1), ret);
BDBOP(penv->set_lk_max_lockers(2000), ret);
BDBOP(penv->set_lk_max_locks(2000), ret);
BDBOP(penv->set_lk_max_objects(2000), ret);
BDBOP2(penv->open(env_home, oflags, mode), ret, penv->close(0));
stack<DbTxn*> stk;
DbTxn *ptxn = NULL;
if (oflags & DB_INIT_CDB) {
BDBOP2(penv->cdsgroup_begin(&ptxn), ret, ptxn->commit(0));
stk.push(ptxn);
}
env_txns_.insert(make_pair(penv, stk));
global_lock(mtx_handle_);
open_envs_.insert(make_pair(penv, 1u));
delenvs.insert(penv);
global_unlock(mtx_handle_);
return penv;
}
DbTxn* ResourceManager::current_txn(DbEnv*env)
{
if (env_txns_.count(env) <= 0)
return NULL;
stack<DbTxn*> &pstk = env_txns_[env];
return pstk.size() != 0 ? pstk.top() : NULL;
}
void ResourceManager::thread_exit()
{
ResourceManager *pinst;
pinst = ResourceManager::instance();
if (pinst == NULL) // Already deleted.
return;
global_lock(mtx_globj_);
glob_objs_.erase(pinst);
global_unlock(mtx_globj_);
TlsWrapper<ResourceManager>::set_tls_obj(NULL);
// Can't put this line between the lock and unlock pair above,
// because the destructor also locks the same mutex, there
// would be a self lock.
delete pinst;
}
void ResourceManager::set_global_callbacks()
{
DbstlElemTraits<char> * cstarinst =
DbstlElemTraits<char>::instance();
cstarinst->set_sequence_len_function(dbstl_strlen);
cstarinst->set_sequence_copy_function(dbstl_strcpy);
cstarinst->set_sequence_compare_function(dbstl_strcmp);
cstarinst->set_sequence_n_compare_function(dbstl_strncmp);
DbstlElemTraits<wchar_t> *wcstarinst =
DbstlElemTraits<wchar_t>::instance();
wcstarinst->set_sequence_copy_function(dbstl_wcscpy);
wcstarinst->set_sequence_len_function(dbstl_wcslen);
wcstarinst->set_sequence_compare_function(dbstl_wcscmp);
wcstarinst->set_sequence_n_compare_function(dbstl_wcsncmp);
}
ResourceManager* ResourceManager::instance()
{
ResourceManager *pinst = NULL;
#ifdef HAVE_PTHREAD_TLS
// Initialize the tls key.
pthread_once(&once_control_, tls_init_once<ResourceManager>);
#endif
if ((pinst = TlsWrapper<ResourceManager>::get_tls_obj()) == NULL){
TlsWrapper<ResourceManager>::set_tls_obj(
pinst = new ResourceManager());
register_global_object(pinst);
set_global_callbacks();
}
return pinst;
}
int ResourceManager::open_cursor(DbCursorBase *dcbcsr,
Db *pdb, int flags)
{
u_int32_t oflags = 0;
int ret;
if (!pdb || !dcbcsr)
return 0;
csrset_t::iterator csitr;
Dbc* csr = NULL;
dcbcsr->set_owner_db(pdb);
DbTxn *ptxn = NULL;
DbTxn *ptxn2 = this->current_txn(pdb->get_env());
if (ptxn2) {
ptxn = ptxn2;
dcbcsr->set_owner_txn(ptxn);
}
if (pdb->get_env() != NULL){
ret = pdb->get_env()->get_open_flags(&oflags);
dbstl_assert(ret == 0);
}
// Call Dbc->cursor only if there is no active open cursor in the
// current thread, otherwise duplicate one from the existing cursor
// and use the locks already held in this thread.
//
csrset_t *pcsrset = NULL;
db_csr_map_t::iterator itrpcsrset = all_csrs_.find(pdb);
if (itrpcsrset == all_csrs_.end()) { // No such pair in current thread.
pcsrset = new csrset_t;
pair<db_csr_map_t::iterator, bool> insret0 =
all_csrs_.insert(make_pair(pdb, pcsrset));
assert(insret0.second);
} else
pcsrset = itrpcsrset->second;
assert(pcsrset != NULL);
if (pcsrset->size() == 0) {
newcursor:
BDBOP2(pdb->cursor(ptxn, &csr, flags), ret,
((csr != NULL ? csr->close() : 1),
this->abort_txn(pdb->get_env())));
} else {
// We have some open cursors, so try to dup from one. If we are
// in CDS mode, and trying to open a write cursor, we should
// duplicate from a write cursor.
csitr = pcsrset->begin();
Dbc *csr22 = (*csitr)->get_cursor();
assert(csr22 != NULL);
assert(!((oflags & DB_INIT_TXN) && (flags & DB_WRITECURSOR)));
// If opening a CDS write cursor, must find a write cursor
// to duplicate from.
if (((flags & DB_WRITECURSOR) != 0)) {
for (;csitr != pcsrset->end(); ++csitr) {
csr22 = (*csitr)->get_cursor();
if (((DBC*)csr22)->flags & DBC_WRITECURSOR) {
// No need to abortTxn on fail in CDS.
BDBOP2(csr22->dup(&csr, DB_POSITION),
ret, csr->close());
goto done;
}
}
goto newcursor; // No write cursor, create a new one.
} else if (((oflags & DB_INIT_TXN) == 0) ||
pdb->get_transactional() == 0) {
// We are opening a DS or CDS read cursor, or
// opening a cursor in
// a transactional environment from a database not
// transactionally created.
BDBOP2(csr22->dup(&csr, DB_POSITION), ret,
(csr->close(), this->abort_txn(pdb->get_env())));
goto done;
} else {
// We are opening a transactional cursor, duplicate
// from a transactional one.
// We don't remove (close) the non-transactional ones,
// they are in use.
// Hold the locks already held in this thread,
// so need DB_POSITION flag.
//
DbTxn *ptxn3 = NULL;
DbCursorBase *dcbcursor = NULL;
csrset_t::iterator itr3, itr4;
int got_rg = 0;
// Opening a cursor in a transactional environment
// with no transaction specified. This should not
// happen in the first place.
if (ptxn == NULL)
THROW(InvalidArgumentException, ("DbTxn*",
"Opening a cursor in a transactional environment but no transaction \
is started specified"));
// When we check that there must be a valid transaction
// handle ptxn when opening a cursor in a
// transactional environment, the following code
// to delete cursors with no transaction
// is not required and never reached,
// but we will leave it there.
for (;csitr != pcsrset->end();) {
dcbcursor = *csitr;
ptxn3 = dcbcursor->get_owner_txn();
if (ptxn3 == NULL) {
BDBOP(dcbcursor->close(), ret);
if (!got_rg){
got_rg++;
itr3 = csitr;
}
} else if (got_rg) {
got_rg = 0;
itr4 = csitr;
pcsrset->erase(itr3, itr4);
csitr = pcsrset->begin();
continue;
}
if (ptxn3 == ptxn) {
csr22 = dcbcursor->get_cursor();
BDBOP2(csr22->dup(&csr, DB_POSITION),
ret, (csr->close(), this->
abort_txn(pdb->get_env())));
goto done;
}
++csitr;
}
if (got_rg) {
pcsrset->erase(itr3, pcsrset->end());
got_rg = 0;
}
goto newcursor;
} // else oflags & DB_INIT_TXN
} // else pcsrset->size()
done:
// Insert into current thread's db-cursor map and txn_csrs_ map,
// for later duplication.
//
dcbcsr->set_cursor(csr);
this->add_cursor(pdb, dcbcsr);
return 0;
}
void ResourceManager::add_cursor(Db* dbp, DbCursorBase* dcbcsr)
{
if (!dbp || !dcbcsr)
return;
assert(dcbcsr->get_cursor() != NULL);
(all_csrs_[dbp])->insert(dcbcsr);
// Register to txncsrs_, we suppose current transaction is the context
// of this operation.
//
this->add_txn_cursor(dcbcsr, dbp->get_env());
}
// Close dbp's all open cursors opened in current thread, do not close
// those of dbp opened in other threads, Db::truncate requires dbp's
// all cursors of all threads should be closed, and it is user's duty
// to make sure other threads all close dbp's cursor, because if we
// close them here, it is also an error---multi-threaded access to the same
// Dbc* cursor should be serialized, we can't serialize with user code
// anyway.
//
size_t ResourceManager::close_db_cursors(Db* dbp1)
{
int ret;
Db* dbp;
DbTxn *ptxn, *ptxn2;
csrset_t *pcset_txn;
if (dbp1 == NULL)
return 0;
dbp = dbp1;
db_csr_map_t::iterator itr0;
csrset_t::iterator itr;
itr0 = all_csrs_.find(dbp1);
if (itr0 == all_csrs_.end())
return 0;
csrset_t *pcset = itr0->second;
pcset_txn = NULL;
ptxn2 = ptxn = NULL;
size_t txncsr_sz = txn_csrs_.size();
for (itr = pcset->begin(), ret = 0; itr != pcset->end();
++itr, ret++) {
BDBOP((*itr)->close(), ret);
if (txncsr_sz > 0) {
if (pcset_txn == NULL || ptxn !=
(ptxn2 = (*itr)->get_owner_txn())) {
ptxn = ptxn2 ? ptxn2 : (*itr)->get_owner_txn();
if (ptxn != NULL)
pcset_txn = txn_csrs_[ptxn];
}
if (pcset_txn)
pcset_txn->erase(*itr);
}
}
// Don't delete the pcset or itr0 because this dbp1 may be used
// by other containers in this thread.
pcset->clear();
// We don't delete the DbCursorBase object, it is still
// referenced by others.
return ret;
}
// Close the cursor of csr and remove the entry containing csr from
// txn_csrs_ and all_csrs_.
int ResourceManager::remove_cursor(DbCursorBase*csr,
bool remove_from_txncsrs)
{
int ret;
if (csr == NULL)
return 0;
BDBOP(csr->close(), ret);
if (remove_from_txncsrs) {
DbTxn *ptxn = csr->get_owner_txn();
if (ptxn != NULL) {
txncsr_t::iterator itr = txn_csrs_.find(ptxn);
if (itr != txn_csrs_.end())
itr->second->erase(csr);
}
}
Db *pdb = csr->get_owner_db();
if (pdb != NULL)
all_csrs_[pdb]->erase(csr);
return ret;
}
/*
* Remove cursors opened in transaction txn's context, should be called before
* commiting/aborting a transaction.
* Note that here we should remove the cursor from all_csrs_ too,
* by calling remove_cursor() function.
*/
void ResourceManager::remove_txn_cursor(DbTxn* txn)
{
int ret;
if (!txn)
return;
txncsr_t::iterator itr0;
csrset_t::iterator itr;
itr0 = txn_csrs_.find(txn);
if (itr0 == txn_csrs_.end())
return; // No cursor opened in this txn.
csrset_t *pcsrs = itr0->second;
DbCursorBase *csr;
// Remove(close and remove from csr registry) cursors
// opened in the transaction txn's context.
for (itr = pcsrs->begin(); itr != pcsrs->end(); ++itr) {
// This cursor should be closed now and removed
// from csr registry.
csr = *itr;
BDBOP(csr->close(), ret);
all_csrs_[csr->get_owner_db()]->erase(csr);
}
delete pcsrs;
// Erase csrs belonging to txn.
txn_csrs_.erase(itr0);
}
// Begin a new transaction from the specified environment env.
// When outtxn is non-zero, it supports nested txn,
// so the new transaction is started as a child transaction of the
// current one, and we push it into env1's transaction stack;
// Otherwise, we are starting an internal transaction for autocommit,
// no new transaction will be started, but current transaction's reference
// count will be incremented.
DbTxn* ResourceManager::begin_txn(u_int32_t flags, DbEnv*env1, int outtxn)
{
DbEnv *env = env1;
DbTxn *ptxn, *txn = NULL;
int ret;
if (!env1)
return NULL;
assert(env_txns_.count(env1) > 0);
stack<DbTxn*>&stk = env_txns_[env1];
// Not an outside transaction, so if there is transaction in stack,
// use it and increment its reference count.
if (outtxn == 0) {
// We have a transaction in stack, increment its reference
// count.
if (stk.size() > 0) {
txn = stk.top();
// The txn was created externally, now we internally
// use it, so the reference count is 2.
map<DbTxn *, size_t>::iterator itr12;
if ((itr12 = txn_count_.find(txn)) == txn_count_.end())
txn_count_.insert(make_pair(txn, 2u));
else
txn_count_[txn]++;
} else {
// Empty stack, create a transaction and set reference count to 1.
BDBOP(env->txn_begin(NULL, &txn, flags), ret);
stk.push(txn);
txn_count_[txn] = 1;// the first to use it
txn_csrs_.insert(make_pair(txn, new csrset_t()));
}
} else { // Creating a transaction by user, used outside of dbstl.
ptxn = stk.size() > 0 ? stk.top() : NULL;
BDBOP(env->txn_begin(ptxn, &txn, flags), ret);
// txn now is the current txn
stk.push(txn);
txn_csrs_.insert(make_pair(txn, new csrset_t()));
}
return txn;
}
void ResourceManager::commit_txn(DbEnv *env, u_int32_t flags)
{
int ret;
DbTxn *ptxn;
if (!env)
return;
assert(env_txns_.count(env) > 0);
stack<DbTxn*> &stk = env_txns_[env];
ptxn = stk.top();
assert(ptxn != NULL);
size_t txncnt = txn_count_[ptxn];
if (txncnt > 1) // used internally
txn_count_[ptxn]--;
else {
txn_count_.erase(ptxn);
this->remove_txn_cursor(ptxn);
stk.pop();
BDBOP(ptxn->commit(flags), ret);
}
}
void ResourceManager::commit_txn(DbEnv *env, DbTxn *txn, u_int32_t flags)
{
DbTxn *ptxn = NULL;
int ret;
if (env == NULL || txn == NULL)
return;
stack<DbTxn*> &stk = env_txns_[env];
while (stk.size() > 0 && (ptxn = stk.top()) != txn) {
stk.pop();
txn_count_.erase(ptxn);// may be in the txn_count_ map
this->remove_txn_cursor(ptxn);
// Child txns could be committed by parent txn, but c++ API
// can't delete the new'ed child txns when committing the
// parent txn, so have to commit them explicitly.
ptxn->commit(flags);
}
if (stk.size() == 0)
THROW(InvalidArgumentException, (
"No such transaction created by dbstl"));
else {
stk.pop();
txn_count_.erase(txn);// may be in the txn_count_ map
this->remove_txn_cursor(txn);
if (ptxn){
BDBOP(ptxn->commit(flags), ret);
} else // could never happen
THROW(InvalidArgumentException, (
"No such transaction created by dbstl"));
}
}
void ResourceManager::abort_txn(DbEnv *env, DbTxn *txn)
{
int ret;
DbTxn *ptxn = NULL;
u_int32_t oflags;
if (env == NULL || txn == NULL)
return;
BDBOP (env->get_open_flags(&oflags), ret);
stack<DbTxn*> &stk = env_txns_[env];
while (stk.size() > 0 && (ptxn = stk.top()) != txn) {
txn_count_.erase(ptxn);// may be in the txn_count_ map
this->remove_txn_cursor(ptxn);
stk.pop();
// Child txns could be aborted by parent txn, but c++ API
// can't delete the new'ed child txns when aborting the
// parent txn, so have to abort them explicitly.
ptxn->abort();
}
if (stk.size() == 0)
THROW(InvalidArgumentException, (
"No such transaction created by dbstl"));
else {
stk.pop();
txn_count_.erase(txn);// may be in the txn_count_ map
this->remove_txn_cursor(txn);
if (ptxn){
if ((oflags & DB_INIT_CDB) == 0)
BDBOP(ptxn->abort(), ret);
} else // could never happen
THROW(InvalidArgumentException, (
"No such transaction created by dbstl"));
}
}
// Abort current txn, close/remove its cursors and reference count.
void ResourceManager::abort_txn(DbEnv*env)
{
int ret;
DbTxn *ptxn;
u_int32_t oflags;
if (!env)
return;
env_txns_t::iterator itr(env_txns_.find(env));
if (itr == env_txns_.end())
return;
stack<DbTxn*> &stk = itr->second;
if (stk.size() == 0)
return;
ptxn = stk.top();
if (ptxn == NULL)
return;
this->remove_txn_cursor(ptxn);
BDBOP (env->get_open_flags(&oflags), ret);
// Transactions handles created via cdsgroup_begin can not be aborted
// because they are not really transactions, they just borrow the
// DB_TXN structure to store a locker id.
if ((oflags & DB_INIT_CDB) == 0)
BDBOP(ptxn->abort(), ret);
txn_count_.erase(ptxn);
stk.pop();
}
DbTxn* ResourceManager::set_current_txn_handle(DbEnv *env, DbTxn *newtxn)
{
assert(env_txns_.count(env) > 0);
stack<DbTxn*> &stk = env_txns_[env];
DbTxn *ptxn = stk.top();
stk.pop();
stk.push(newtxn);
return ptxn;
}
void ResourceManager::add_txn_cursor(DbCursorBase *dcbcsr, DbEnv *env)
{
if (!env || !dcbcsr)
return;
DbTxn *ptxn = this->current_txn(env);
if (ptxn == NULL)
return;
u_int32_t oflags;
int ret;
BDBOP(env->get_open_flags(&oflags), ret);
if ((oflags & DB_INIT_TXN) == 0)
return;
txncsr_t::iterator itr;
csrset_t *pset;
itr = txn_csrs_.find(ptxn);
pair<txncsr_t::iterator, bool> insret;
if (itr == txn_csrs_.end()) {
insret = txn_csrs_.insert(make_pair(ptxn, new csrset_t()));
assert(insret.second);
itr = insret.first;
}
pset = itr->second;
pset->insert(dcbcsr);
}
void ResourceManager::register_db(Db*pdb1)
{
if (!pdb1)
return;
global_lock(mtx_handle_);
if (open_dbs_.count(pdb1) == 0)
open_dbs_.insert(make_pair(pdb1, 1u));
else
open_dbs_[pdb1]++;
global_unlock(mtx_handle_);
csrset_t *pcsrset = new csrset_t();
pair<db_csr_map_t::iterator, bool> insret = all_csrs_.insert(
make_pair(pdb1, pcsrset));
if (!insret.second)
delete pcsrset;
}
void ResourceManager::register_db_env(DbEnv*env1)
{
u_int32_t oflags = 0;
DbTxn *ptxn = NULL;
int ret;
if (!env1)
return;
stack<DbTxn*> stk;
BDBOP(env1->get_open_flags(&oflags), ret);
if (oflags & DB_INIT_CDB) {
env1->cdsgroup_begin(&ptxn);
stk.push(ptxn);
}
env_txns_.insert(make_pair(env1, stk));
global_lock(mtx_handle_);
if (open_envs_.count(env1) == 0)
open_envs_.insert(make_pair(env1, 1u));
else
open_envs_[env1]++;
global_unlock(mtx_handle_);
}
// Delete registered DbstlGlobalInnerObject objects.
void ResourceManager::global_exit()
{
set<DbstlGlobalInnerObject *>::iterator itr;
global_lock(mtx_globj_);
for (itr = glob_objs_.begin(); itr != glob_objs_.end(); ++itr)
delete *itr;
global_unlock(mtx_globj_);
mtx_env_->mutex_free(mtx_globj_);
mtx_env_->mutex_free(mtx_handle_);
delete mtx_env_;
}
void ResourceManager::register_global_object(DbstlGlobalInnerObject *gio)
{
global_lock(mtx_globj_);
glob_objs_.insert(gio);
global_unlock(mtx_globj_);
}
END_NS