1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2009 Oracle. All rights reserved. 5 * 6 * $Id$ 7 */ 8 9#ifndef _DB_STL_RESOURCE_MANAGER_H__ 10#define _DB_STL_RESOURCE_MANAGER_H__ 11 12#include <map> 13#include <vector> 14#include <stack> 15#include <set> 16 17#include "dbstl_common.h" 18#include "dbstl_inner_utility.h" 19 20START_NS(dbstl) 21 22class DbCursorBase; 23using std::map; 24using std::multimap; 25using std::set; 26using std::stack; 27 28/////////////////////////////////////////////////////////////////////// 29///////////////////////////////////////////////////////////////////////// 30// 31// ResourceManager class definition 32// 33// This class manages all the Berkeley DB handles and their mapping 34// relationship. When it's only thread-specific instance is destructed, 35// these handles are automatically closed in the correct order (the db and 36// dbenv handles will be closed when the last thread using the handles in 37// the process is destructed). 38// 39// The Db* and DbEnv* handles are process-wide global, they can be shared 40// among multithreads, so they are stored into a static stl map, and access 41// to the two map objects (open_dbs_ & open_envs_) is protected by a process 42// wide mutex because few stl implementations support multithreaded access. 43// We use reference counting in the two map objects to make sure each handle 44// is closed when the last thread using it exits. Each thread sharing the 45// handle should call ResourceManager::register_db/dbenv to tell DBSTL that 46// it will use the handle, otherwise the handle may be closed prematurely. 47// 48// The transaction and cursor handles are thread specific. They are stored 49// into stl containers and each instance of the ResourceManager is stored 50// into thread local storage(TLS) of each thread. Thus the DB STL containers 51// are thread safe. 52// 53 54// This map contains cursors of all open databases opened in the same thread. 55// We can only duplicate a cursor of the same database; We don't allow sharing 56// Berkeley DB cursor handles across multiple threads, each thread needs to 57// open their own cursor handle; 58// 59typedef map<Db *, set<dbstl::DbCursorBase *> *> db_csr_map_t; 60 61// The set of cursors that belong to a db handle or a transaction. 62typedef set<dbstl::DbCursorBase *> csrset_t; 63// The cursors opened in each transaction. 64typedef map<DbTxn *, set<DbCursorBase *> *> txncsr_t; 65 66// This stack contains the transactions started in current thread. Each 67// transaction is the child transaction of the one under it in the stack. 68// 69// We support nested transactions for those created by the dbstl 70// users, but still keep reference counting for dbstl internally 71// created transactions so that the autocommit methods are really 72// autocommit with least overhead (nested transactions are overheads). The 73// way we keep both nested transaction and reference counting to internal 74// transactions in the same stack is: 75// 1. When creating an external transaction, look at the stack top, if there 76// is a transaction, it must be an external one too, so use it as the parent 77// transaction to create the external transaction. 78// 2. When creating an internal transaction, look at the stack top, if there 79// is one, call it T, look for its the reference counting, if there is a 80// reference count for it, T is an internal one, so we increment its 81// reference count; Otherwise, T is an external one, and according to the DB 82// autocommit semantics, this function should be in T's context, so we 83// simply use T and add it to the reference counting structure, and set its 84// reference count to 2. 85// 86// We don't support expanding a transaction across multiple threads, 87// because there are many restrictions to doing so, making it meaningless. 88// 89typedef stack<DbTxn *> txnstk_t; 90typedef map<DbEnv *, txnstk_t> env_txns_t; 91 92#ifdef WIN32 93#pragma warning( push ) 94#pragma warning( disable:4251 ) 95#endif 96// This class is used to wrap a ResourceManager instance pointer, so that 97// each thread has its own ResourceManager singleton. 98 99#ifdef TLS_DECL_MODIFIER 100template <Typename T> 101class TlsWrapper 102{ 103public: 104 static T *get_tls_obj() 105 { 106 return tinst_; 107 } 108 109 static void set_tls_obj(T *objp) 110 { 111 tinst_ = objp; 112 } 113 114private: 115 TlsWrapper(){} 116 117 // Thread local pointer to the instance of type T. 118 static TLS_DECL_MODIFIER T *tinst_; 119}; // TlsWrapper<> 120 121#elif defined(HAVE_PTHREAD_TLS) 122template <Typename T> 123class TlsWrapper 124{ 125public: 126 static T *get_tls_obj() 127 { 128 return static_cast<T*>(pthread_getspecific(tls_key_)); 129 } 130 131 static void set_tls_obj(T *objp) 132 { 133 pthread_setspecific(tls_key_, objp); 134 } 135 136 // Friend declarations don't work portably, so we have to declare 137 // tls_key_ public. 138 static pthread_key_t tls_key_; 139private: 140 TlsWrapper(); 141 142}; // TlsWrapper<> 143 144#else 145#error "A multi-threaded build of STL for Berkeley DB requires thread local storage. None is configured." 146#endif 147 148class _exported ResourceManager : public DbstlGlobalInnerObject 149{ 150private: 151 152 ResourceManager(void); 153 // open_dbs_ & open_envs_ are shared among threads, protected by 154 // ghdl_mtx; 155 static map<Db *, size_t> open_dbs_; 156 static map<DbEnv *, size_t>open_envs_; 157 158 // Transaction stack of all environments. Use a stack to allow nested 159 // transactions. The transaction at the top of the stack is the 160 // current active transaction. 161 // 162 env_txns_t env_txns_; 163 164 // Cursors opened in a corresponding transaction context. When 165 // committing or aborting a transaction, first close all open cursors. 166 // 167 txncsr_t txn_csrs_; 168 169 // If in container X, its methods X::A and X::B both call begin and 170 // commit transaction. X::A calls X::B after it's begin transaction 171 // call, then X::B will commit the transaction prematurally. To avoid 172 // will commit the only transaction prematurally, to avoid this, we use 173 // this, we use this map to record each transaction's reference count. 174 // Each begin/commit_txn() will increment/decrement the reference 175 // count, when reference count goes to 0, the transaction is committed. 176 // Abort_txn will unconditionally abort the transaction. 177 // 178 map<DbTxn *, size_t> txn_count_; 179 180 // Contains the cursors opened in the current thread for each database, 181 // So that we can close them in the right way, freeing any Berkeley DB 182 // handles before exiting. 183 // 184 db_csr_map_t all_csrs_; 185 186 // Remove cursors opened in the transaction txn's context, should be 187 // called before commiting or aborting a transaction. 188 // 189 void remove_txn_cursor(DbTxn *txn); 190 191 // Add a cursor to the current transaction's set of open cursors. 192 void add_txn_cursor(DbCursorBase *dcbcsr, DbEnv *env); 193 194 // The environment mtx_env_ and mtx_handle_ are used for synchronizing 195 // multiple threads' access to open_dbs_ and open_envs_ and glob_objs_. 196 // They are discarded when program exits, no deallocation/release 197 // is done. 198 static DbEnv *mtx_env_; 199 static db_mutex_t mtx_handle_; 200 static db_mutex_t mtx_globj_; 201 static set<DbstlGlobalInnerObject *> glob_objs_; 202 203 // This set stores db handles that are new'ed by open_db, and thus 204 // should be deleted after this db is closed automatically by dbstl. 205 // If a db is new'ed and created by user without using open_db, users 206 // should delete it. 207 static set<Db *> deldbs; 208 static set<DbEnv *> delenvs; // Similar to deldbs, works with open_envs. 209 static void set_global_callbacks(); 210public: 211 212 // This function should be called in a single thread inside a process, 213 // before any use of dbstl. 214 static void global_startup(); 215 // Delete registered DbstlGlobalInnerObject objects. 216 static void global_exit(); 217 static void register_global_object(DbstlGlobalInnerObject *gio); 218 static DbEnv *get_mutex_env() { return mtx_env_; } 219 // Lock mtx_handle_, if it is 0, allocate it first. 220 static int global_lock(db_mutex_t dbcontainer_mtx); 221 static int global_unlock(db_mutex_t dbcontainer_mtx); 222 // Close pdb regardless of its reference count, users must make sure 223 // pdb is not used by others before calling this method. We can't 224 // close by reference count in this method, otherwise when the thread 225 // exits pdb's reference count is decremented twice. 226 void close_db(Db *pdb); 227 228 // Close all db handles regardless of reference count, used to clean up 229 // the calling thread's ResourceManager singleton. 230 void close_all_dbs(); 231 232 // Close specified db env handle and remove it from resource manager. 233 void close_db_env(DbEnv *penv); 234 235 // Close and remove all db env registered in the resource manager. 236 // Used to clean up the calling thread's ResourceManager singleton. 237 void close_all_db_envs(); 238 239 // Begin a new transaction in the specified environment. When outtxn 240 // is non-zero support nested transactions - the new transaction will 241 // be started as a child of the current transaction. If outtxn is 0 242 // we are starting an internal transaction for autocommit, no new 243 // transaction will be started, but the current transaction's 244 // reference count will be incremented if it already has a reference 245 // count; otherwise, it was created by user, and we simply use this 246 // transaction, set its reference count to 2. 247 // 248 // This function is called by both containers to begin an internal 249 // transaction for autocommit, and by db stl users to begin an 250 // external transaction. 251 // 252 DbTxn *begin_txn(u_int32_t flags/* flags for DbEnv::txn_begin */, 253 DbEnv *env, int outtxn); 254 255 // Decrement reference count if it exists and if it goes to 0, commit 256 // current transaction T opened in env; 257 // If T has no reference count(outside transaction), simply find 258 // it by popping the stack and commit it. 259 // 260 // This function is called by db_container to commit a transaction 261 // for auto commit, also can be called by db stl user to commit 262 // an external explicit transaction. 263 // 264 void commit_txn(DbEnv *env, u_int32_t flags = 0); 265 266 // Commit specified transaction txn: find it by popping the stack, 267 // discard all its child transactions and commit it. 268 // 269 // This function is called by dbstl user to commit an external 270 // explicit transaction. 271 // 272 void commit_txn(DbEnv *env, DbTxn *txn, u_int32_t flags = 0); 273 274 // Abort current transaction of the environment. 275 // 276 void abort_txn(DbEnv *env); 277 278 // Abort specified transaction: find it by popping the stack, discard 279 // all its child transactions and abort it. 280 // 281 // This function is called by dbstl user to abort an external 282 // explicit transaction. 283 // 284 void abort_txn(DbEnv *env, DbTxn *txn); 285 286 // Set env's current transaction handle. The original transaction 287 // handle is returned without aborting or commiting. This API can be 288 // used to share a transaction handle among multiple threads. 289 DbTxn* set_current_txn_handle(DbEnv *env, DbTxn *newtxn); 290 291 // Register a Db handle. This handle and handles opened in it 292 // will be closed by ResourceManager, so application code must not 293 // try to close or delete it. Users can configure the handle before 294 // opening the Db and then register it via this function. 295 // 296 void register_db(Db *pdb1); 297 298 // Register a DbEnv handle. This handle and handles opened in it 299 // will be closed by ResourceManager, so application code must not try 300 // to close or delete it. Users can configure the handle before 301 // opening the Environment and then register it via this function. 302 // 303 void register_db_env(DbEnv *env1); 304 305 // Helper function to open a database and register it into 306 // ResourceManager. 307 // Users can set the create flags, open flags, db type, and flags 308 // needing to be set before open. 309 // 310 Db* open_db (DbEnv *penv, const char *filename, DBTYPE dbtype, 311 u_int32_t oflags, u_int32_t set_flags, int mode = 0644, 312 DbTxn* txn = NULL, u_int32_t cflags = 0, 313 const char *dbname = NULL); 314 315 // Helper function to open a dbenv and register it into 316 // ResourceManager. 317 // Users can set the create flags, open flags, db type, and flags 318 // needing to be set before open. 319 // 320 DbEnv *open_env(const char *env_home, u_int32_t set_flags, 321 u_int32_t oflags = DB_CREATE | DB_INIT_MPOOL, 322 u_int32_t cachesize = 4 * 1024 * 1024, int mode = 0644, 323 u_int32_t cflags = 0/* Flags for DbEnv constructor. */); 324 325 static ResourceManager *instance(); 326 327 // Release all registered resource in the right order. 328 virtual ~ResourceManager(void); 329 330 // Return current transaction of environment env, that is, the one on 331 // the transaction stack top, the active one. 332 // 333 DbTxn *current_txn(DbEnv *env); 334 335 // Open a Berkeley DB cursor. 336 // 337 int open_cursor(DbCursorBase *dcbcsr, Db *pdb, int flags = 0); 338 339 // Add a db-cursor mapping. 340 void add_cursor(Db *dbp, DbCursorBase *csr); 341 342 // Close all cursors opened in the database. 343 size_t close_db_cursors(Db *dbp1); 344 345 // Close and remove a cursor from ResourceManager. 346 // 347 int remove_cursor(DbCursorBase *csr, bool remove_from_txncsrs = true); 348 349}; // ResourceManager 350 351END_NS 352#ifdef WIN32 353#pragma warning( pop ) 354#endif 355#endif // !_DB_STL_RESOURCE_MANAGER_H__ 356