libdb/lang/cxx/stl/dbstl_resource_manager.h

359 lines
13 KiB
C++

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2009, 2011 Oracle and/or its affiliates. All rights reserved.
*
* $Id$
*/
#ifndef _DB_STL_RESOURCE_MANAGER_H__
#define _DB_STL_RESOURCE_MANAGER_H__
#include <map>
#include <vector>
#include <stack>
#include <set>
#include "dbstl_common.h"
#include "dbstl_inner_utility.h"
START_NS(dbstl)
class DbCursorBase;
using std::map;
using std::multimap;
using std::set;
using std::stack;
///////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
// ResourceManager class definition
//
// This class manages all the Berkeley DB handles and their mapping
// relationship. When it's only thread-specific instance is destructed,
// these handles are automatically closed in the correct order (the db and
// dbenv handles will be closed when the last thread using the handles in
// the process is destructed).
//
// The Db* and DbEnv* handles are process-wide global, they can be shared
// among multithreads, so they are stored into a static stl map, and access
// to the two map objects (open_dbs_ & open_envs_) is protected by a process
// wide mutex because few stl implementations support multithreaded access.
// We use reference counting in the two map objects to make sure each handle
// is closed when the last thread using it exits. Each thread sharing the
// handle should call ResourceManager::register_db/dbenv to tell DBSTL that
// it will use the handle, otherwise the handle may be closed prematurely.
//
// The transaction and cursor handles are thread specific. They are stored
// into stl containers and each instance of the ResourceManager is stored
// into thread local storage(TLS) of each thread. Thus the DB STL containers
// are thread safe.
//
// This map contains cursors of all open databases opened in the same thread.
// We can only duplicate a cursor of the same database; We don't allow sharing
// Berkeley DB cursor handles across multiple threads, each thread needs to
// open their own cursor handle;
//
typedef map<Db *, set<dbstl::DbCursorBase *> *> db_csr_map_t;
// The set of cursors that belong to a db handle or a transaction.
typedef set<dbstl::DbCursorBase *> csrset_t;
// The cursors opened in each transaction.
typedef map<DbTxn *, set<DbCursorBase *> *> txncsr_t;
// This stack contains the transactions started in current thread. Each
// transaction is the child transaction of the one under it in the stack.
//
// We support nested transactions for those created by the dbstl
// users, but still keep reference counting for dbstl internally
// created transactions so that the autocommit methods are really
// autocommit with least overhead (nested transactions are overheads). The
// way we keep both nested transaction and reference counting to internal
// transactions in the same stack is:
// 1. When creating an external transaction, look at the stack top, if there
// is a transaction, it must be an external one too, so use it as the parent
// transaction to create the external transaction.
// 2. When creating an internal transaction, look at the stack top, if there
// is one, call it T, look for its the reference counting, if there is a
// reference count for it, T is an internal one, so we increment its
// reference count; Otherwise, T is an external one, and according to the DB
// autocommit semantics, this function should be in T's context, so we
// simply use T and add it to the reference counting structure, and set its
// reference count to 2.
//
// We don't support expanding a transaction across multiple threads,
// because there are many restrictions to doing so, making it meaningless.
//
typedef stack<DbTxn *> txnstk_t;
typedef map<DbEnv *, txnstk_t> env_txns_t;
#ifdef WIN32
#pragma warning( push )
#pragma warning( disable:4251 )
#endif
// This class is used to wrap a ResourceManager instance pointer, so that
// each thread has its own ResourceManager singleton.
#ifdef TLS_DECL_MODIFIER
template <Typename T>
class TlsWrapper
{
public:
static T *get_tls_obj()
{
return tinst_;
}
static void set_tls_obj(T *objp)
{
tinst_ = objp;
}
private:
TlsWrapper(){}
// Thread local pointer to the instance of type T.
static TLS_DECL_MODIFIER T *tinst_;
}; // TlsWrapper<>
#elif defined(HAVE_PTHREAD_TLS)
template <Typename T>
class TlsWrapper
{
public:
static T *get_tls_obj()
{
return static_cast<T*>(pthread_getspecific(tls_key_));
}
static void set_tls_obj(T *objp)
{
pthread_setspecific(tls_key_, objp);
}
// Friend declarations don't work portably, so we have to declare
// tls_key_ public.
static pthread_key_t tls_key_;
private:
TlsWrapper();
}; // TlsWrapper<>
#else
#error "A multi-threaded build of STL for Berkeley DB requires thread local storage. None is configured."
#endif
class _exported ResourceManager : public DbstlGlobalInnerObject
{
private:
ResourceManager(void);
// open_dbs_ & open_envs_ are shared among threads, protected by
// ghdl_mtx;
static map<Db *, size_t> open_dbs_;
static map<DbEnv *, size_t>open_envs_;
// Transaction stack of all environments. Use a stack to allow nested
// transactions. The transaction at the top of the stack is the
// current active transaction.
//
env_txns_t env_txns_;
// Cursors opened in a corresponding transaction context. When
// committing or aborting a transaction, first close all open cursors.
//
txncsr_t txn_csrs_;
// If in container X, its methods X::A and X::B both call begin and
// commit transaction. X::A calls X::B after it's begin transaction
// call, then X::B will commit the transaction prematurally. To avoid
// will commit the only transaction prematurally, to avoid this, we use
// this, we use this map to record each transaction's reference count.
// Each begin/commit_txn() will increment/decrement the reference
// count, when reference count goes to 0, the transaction is committed.
// Abort_txn will unconditionally abort the transaction.
//
map<DbTxn *, size_t> txn_count_;
// Contains the cursors opened in the current thread for each database,
// So that we can close them in the right way, freeing any Berkeley DB
// handles before exiting.
//
db_csr_map_t all_csrs_;
// Remove cursors opened in the transaction txn's context, should be
// called before commiting or aborting a transaction.
//
void remove_txn_cursor(DbTxn *txn);
// Add a cursor to the current transaction's set of open cursors.
void add_txn_cursor(DbCursorBase *dcbcsr, DbEnv *env);
// The environment mtx_env_ and mtx_handle_ are used for synchronizing
// multiple threads' access to open_dbs_ and open_envs_ and glob_objs_.
// They are discarded when program exits, no deallocation/release
// is done.
static DbEnv *mtx_env_;
static db_mutex_t mtx_handle_;
static db_mutex_t mtx_globj_;
static set<DbstlGlobalInnerObject *> glob_objs_;
// This set stores db handles that are new'ed by open_db, and thus
// should be deleted after this db is closed automatically by dbstl.
// If a db is new'ed and created by user without using open_db, users
// should delete it.
static set<Db *> deldbs;
static set<DbEnv *> delenvs; // Similar to deldbs, works with open_envs.
static void set_global_callbacks();
public:
// This function should be called in a single thread inside a process,
// before any use of dbstl.
static void global_startup();
// Delete registered DbstlGlobalInnerObject objects.
static void global_exit();
// Delete the ResourceManager singleton of current thread, and remove it
// from glob_objs.
static void thread_exit();
static void register_global_object(DbstlGlobalInnerObject *gio);
static DbEnv *get_mutex_env() { return mtx_env_; }
// Lock mtx_handle_, if it is 0, allocate it first.
static int global_lock(db_mutex_t dbcontainer_mtx);
static int global_unlock(db_mutex_t dbcontainer_mtx);
// Close pdb regardless of its reference count, users must make sure
// pdb is not used by others before calling this method. We can't
// close by reference count in this method, otherwise when the thread
// exits pdb's reference count is decremented twice.
void close_db(Db *pdb);
// Close all db handles regardless of reference count, used to clean up
// the calling thread's ResourceManager singleton.
void close_all_dbs();
// Close specified db env handle and remove it from resource manager.
void close_db_env(DbEnv *penv);
// Close and remove all db env registered in the resource manager.
// Used to clean up the calling thread's ResourceManager singleton.
void close_all_db_envs();
// Begin a new transaction in the specified environment. When outtxn
// is non-zero support nested transactions - the new transaction will
// be started as a child of the current transaction. If outtxn is 0
// we are starting an internal transaction for autocommit, no new
// transaction will be started, but the current transaction's
// reference count will be incremented if it already has a reference
// count; otherwise, it was created by user, and we simply use this
// transaction, set its reference count to 2.
//
// This function is called by both containers to begin an internal
// transaction for autocommit, and by db stl users to begin an
// external transaction.
//
DbTxn *begin_txn(u_int32_t flags/* flags for DbEnv::txn_begin */,
DbEnv *env, int outtxn);
// Decrement reference count if it exists and if it goes to 0, commit
// current transaction T opened in env;
// If T has no reference count(outside transaction), simply find
// it by popping the stack and commit it.
//
// This function is called by db_container to commit a transaction
// for auto commit, also can be called by db stl user to commit
// an external explicit transaction.
//
void commit_txn(DbEnv *env, u_int32_t flags = 0);
// Commit specified transaction txn: find it by popping the stack,
// discard all its child transactions and commit it.
//
// This function is called by dbstl user to commit an external
// explicit transaction.
//
void commit_txn(DbEnv *env, DbTxn *txn, u_int32_t flags = 0);
// Abort current transaction of the environment.
//
void abort_txn(DbEnv *env);
// Abort specified transaction: find it by popping the stack, discard
// all its child transactions and abort it.
//
// This function is called by dbstl user to abort an external
// explicit transaction.
//
void abort_txn(DbEnv *env, DbTxn *txn);
// Set env's current transaction handle. The original transaction
// handle is returned without aborting or commiting. This API can be
// used to share a transaction handle among multiple threads.
DbTxn* set_current_txn_handle(DbEnv *env, DbTxn *newtxn);
// Register a Db handle. This handle and handles opened in it
// will be closed by ResourceManager, so application code must not
// try to close or delete it. Users can configure the handle before
// opening the Db and then register it via this function.
//
void register_db(Db *pdb1);
// Register a DbEnv handle. This handle and handles opened in it
// will be closed by ResourceManager, so application code must not try
// to close or delete it. Users can configure the handle before
// opening the Environment and then register it via this function.
//
void register_db_env(DbEnv *env1);
// Helper function to open a database and register it into
// ResourceManager.
// Users can set the create flags, open flags, db type, and flags
// needing to be set before open.
//
Db* open_db (DbEnv *penv, const char *filename, DBTYPE dbtype,
u_int32_t oflags, u_int32_t set_flags, int mode = 0644,
DbTxn* txn = NULL, u_int32_t cflags = 0,
const char *dbname = NULL);
// Helper function to open a dbenv and register it into
// ResourceManager.
// Users can set the create flags, open flags, db type, and flags
// needing to be set before open.
//
DbEnv *open_env(const char *env_home, u_int32_t set_flags,
u_int32_t oflags = DB_CREATE | DB_INIT_MPOOL,
u_int32_t cachesize = 4 * 1024 * 1024, int mode = 0644,
u_int32_t cflags = 0/* Flags for DbEnv constructor. */);
static ResourceManager *instance();
// Release all registered resource in the right order.
virtual ~ResourceManager(void);
// Return current transaction of environment env, that is, the one on
// the transaction stack top, the active one.
//
DbTxn *current_txn(DbEnv *env);
// Open a Berkeley DB cursor.
//
int open_cursor(DbCursorBase *dcbcsr, Db *pdb, int flags = 0);
// Add a db-cursor mapping.
void add_cursor(Db *dbp, DbCursorBase *csr);
// Close all cursors opened in the database.
size_t close_db_cursors(Db *dbp1);
// Close and remove a cursor from ResourceManager.
//
int remove_cursor(DbCursorBase *csr, bool remove_from_txncsrs = true);
}; // ResourceManager
END_NS
#ifdef WIN32
#pragma warning( pop )
#endif
#endif // !_DB_STL_RESOURCE_MANAGER_H__