/**
* Copyright (C) 2008 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
#pragma once
#include "../pch.h"
#include "../util/net/message.h"
#include "concurrency.h"
#include "pdfile.h"
#include "curop.h"
#include "client.h"
namespace mongo {
// void jniCallback(Message& m, Message& out);
/**
* class to hold path + dbname -> Database
* might be able to optimizer further
*/
class DatabaseHolder {
public:
typedef map DBs;
typedef map Paths;
DatabaseHolder() : _size(0) { }
bool isLoaded( const string& ns , const string& path ) const {
dbMutex.assertAtLeastReadLocked();
Paths::const_iterator x = _paths.find( path );
if ( x == _paths.end() )
return false;
const DBs& m = x->second;
string db = _todb( ns );
DBs::const_iterator it = m.find(db);
return it != m.end();
}
Database * get( const string& ns , const string& path ) const {
dbMutex.assertAtLeastReadLocked();
Paths::const_iterator x = _paths.find( path );
if ( x == _paths.end() )
return 0;
const DBs& m = x->second;
string db = _todb( ns );
DBs::const_iterator it = m.find(db);
if ( it != m.end() )
return it->second;
return 0;
}
void put( const string& ns , const string& path , Database * db ) {
dbMutex.assertWriteLocked();
DBs& m = _paths[path];
Database*& d = m[_todb(ns)];
if ( ! d )
_size++;
d = db;
}
Database* getOrCreate( const string& ns , const string& path , bool& justCreated );
void erase( const string& ns , const string& path ) {
dbMutex.assertWriteLocked();
DBs& m = _paths[path];
_size -= (int)m.erase( _todb( ns ) );
}
/* force - force close even if something underway - use at shutdown */
bool closeAll( const string& path , BSONObjBuilder& result, bool force );
int size() {
return _size;
}
void forEach(boost::function f) const {
dbMutex.assertAtLeastReadLocked();
for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); i++ ) {
DBs m = i->second;
for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ) {
f(j->second);
}
}
}
/**
* gets all unique db names, ignoring paths
*/
void getAllShortNames( set& all ) const {
dbMutex.assertAtLeastReadLocked();
for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); i++ ) {
DBs m = i->second;
for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ) {
all.insert( j->first );
}
}
}
private:
string _todb( const string& ns ) const {
string d = __todb( ns );
uassert( 13280 , (string)"invalid db name: " + ns , Database::validDBName( d ) );
return d;
}
string __todb( const string& ns ) const {
size_t i = ns.find( '.' );
if ( i == string::npos ) {
uassert( 13074 , "db name can't be empty" , ns.size() );
return ns;
}
uassert( 13075 , "db name can't be empty" , i > 0 );
return ns.substr( 0 , i );
}
Paths _paths;
int _size;
};
extern DatabaseHolder dbHolder;
struct dbtemprelease {
Client::Context * _context;
int _locktype;
dbtemprelease() {
const Client& c = cc();
_context = c.getContext();
_locktype = dbMutex.getState();
assert( _locktype );
if ( _locktype > 0 ) {
massert( 10298 , "can't temprelease nested write lock", _locktype == 1);
if ( _context ) _context->unlocked();
dbMutex.unlock();
}
else {
massert( 10299 , "can't temprelease nested read lock", _locktype == -1);
if ( _context ) _context->unlocked();
dbMutex.unlock_shared();
}
verify( 14814 , c.curop() );
c.curop()->yielded();
}
~dbtemprelease() {
if ( _locktype > 0 )
dbMutex.lock();
else
dbMutex.lock_shared();
if ( _context ) _context->relocked();
}
};
/** must be write locked
no assert (and no release) if nested write lock
a lot like dbtempreleasecond but no malloc so should be a tiny bit faster
*/
struct dbtempreleasewritelock {
Client::Context * _context;
int _locktype;
dbtempreleasewritelock() {
const Client& c = cc();
_context = c.getContext();
_locktype = dbMutex.getState();
assert( _locktype >= 1 );
if( _locktype > 1 )
return; // nested
if ( _context )
_context->unlocked();
dbMutex.unlock();
verify( 14845 , c.curop() );
c.curop()->yielded();
}
~dbtempreleasewritelock() {
if ( _locktype == 1 )
dbMutex.lock();
if ( _context )
_context->relocked();
}
};
/**
only does a temp release if we're not nested and have a lock
*/
struct dbtempreleasecond {
dbtemprelease * real;
int locktype;
dbtempreleasecond() {
real = 0;
locktype = dbMutex.getState();
if ( locktype == 1 || locktype == -1 )
real = new dbtemprelease();
}
~dbtempreleasecond() {
if ( real ) {
delete real;
real = 0;
}
}
bool unlocked() {
return real > 0;
}
};
} // namespace mongo
//#include "dbinfo.h"
#include "concurrency.h"