mirror of
https://github.com/berkeleydb/libdb.git
synced 2024-11-16 17:16:25 +00:00
359 lines
13 KiB
C++
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__
|