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