0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 01:21:03 +01:00
mongodb/db/client.h

279 lines
7.9 KiB
C
Raw Normal View History

// client.h
/**
* 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 <http://www.gnu.org/licenses/>.
*/
/* Client represents a connection to the database (the server-side) and corresponds
to an open socket (or logical connection if pooling on sockets) from a client.
todo: switch to asio...this will fit nicely with that.
*/
#pragma once
2010-04-27 21:27:52 +02:00
#include "../pch.h"
#include "security.h"
#include "namespace.h"
#include "lasterror.h"
2010-02-01 16:47:28 +01:00
#include "stats/top.h"
namespace mongo {
2009-10-13 22:01:02 +02:00
class AuthenticationInfo;
class Database;
2009-12-21 19:19:20 +01:00
class CurOp;
2009-12-31 22:22:28 +01:00
class Command;
2010-01-17 22:57:35 +01:00
class Client;
extern boost::thread_specific_ptr<Client> currentClient;
2009-10-15 06:56:51 +02:00
class Client : boost::noncopyable {
public:
static mongo::mutex clientsMutex;
static set<Client*> clients; // always be in clientsMutex when manipulating this
2009-10-23 17:13:08 +02:00
static int recommendedYieldMicros();
2009-10-23 17:13:08 +02:00
class GodScope {
bool _prev;
public:
GodScope();
~GodScope();
};
2010-01-17 22:57:35 +01:00
/* Set database we want to use, then, restores when we finish (are out of scope)
Note this is also helpful if an exception happens as the state if fixed up.
*/
class Context : boost::noncopyable{
2010-01-17 22:57:35 +01:00
Client * _client;
Context * _oldContext;
string _path;
mongolock * _lock;
bool _justCreated;
string _ns;
Database * _db;
/**
* at this point _client, _oldContext and _ns have to be set
* _db should not have been touched
* this will set _db and create if needed
* will also set _client->_context to this
*/
void _finishInit( bool doauth=true);
void _auth( int lockState = dbMutex.getState() );
2010-01-17 22:57:35 +01:00
public:
Context(const string& ns, string path=dbpath, mongolock * lock = 0 , bool doauth=true )
: _client( currentClient.get() ) , _oldContext( _client->_context ) ,
_path( path ) , _lock( lock ) ,
_ns( ns ){
_finishInit( doauth );
2010-01-17 22:57:35 +01:00
}
/* this version saves the context but doesn't yet set the new one: */
2010-01-17 22:57:35 +01:00
Context()
: _client( currentClient.get() ) , _oldContext( _client->_context ),
_path( dbpath ) , _lock(0) , _justCreated(false){
_client->_context = this;
clear();
2010-01-17 22:57:35 +01:00
}
/**
* if you are doing this after allowing a write there could be a race condition
* if someone closes that db. this checks that the DB is still valid
*/
Context( string ns , Database * db, bool doauth=true );
~Context();
Client* getClient() const { return _client; }
Database* db() const {
return _db;
2010-01-17 22:57:35 +01:00
}
const char * ns() const {
return _ns.c_str();
}
bool justCreated() const {
return _justCreated;
}
bool equals( const string& ns , const string& path=dbpath ) const {
return _ns == ns && _path == path;
}
bool inDB( const string& db , const string& path=dbpath ) const {
if ( _path != path )
return false;
if ( db == _ns )
return true;
string::size_type idx = _ns.find( db );
if ( idx != 0 )
return false;
return _ns[db.size()] == '.';
}
void clear(){
_ns = "";
_db = 0;
}
/**
* call before unlocking, so clear any non-thread safe state
*/
void unlocked(){
_db = 0;
}
2010-01-17 22:57:35 +01:00
/**
* call after going back into the lock, will re-establish non-thread safe stuff
*/
void relocked(){
_finishInit();
}
friend class CurOp;
};
private:
2010-02-04 21:56:02 +01:00
CurOp * _curOp;
Context * _context;
bool _shutdown;
list<string> _tempCollections;
const char *_desc;
bool _god;
AuthenticationInfo _ai;
2010-03-30 21:20:22 +02:00
OpTime _lastOp;
2010-04-02 17:29:33 +02:00
BSONObj _handshake;
BSONObj _remoteId;
public:
AuthenticationInfo * getAuthenticationInfo(){ return &_ai; }
bool isAdmin() { return _ai.isAuthorized( "admin" ); }
2009-12-08 17:34:57 +01:00
CurOp* curop() { return _curOp; }
Context* getContext(){ return _context; }
Database* database() { return _context ? _context->db() : 0; }
const char *ns() { return _context->ns(); }
Client(const char *desc);
2009-10-13 22:01:02 +02:00
~Client();
2009-12-07 21:42:26 +01:00
const char *desc() const { return _desc; }
void addTempCollection( const string& ns ){
_tempCollections.push_back( ns );
}
2010-03-30 21:20:22 +02:00
2010-05-06 17:51:21 +02:00
void dropTempCollectionsInDB(const string db);
void dropAllTempCollectionsInDB(const string db);
2010-03-30 21:20:22 +02:00
void setLastOp( const OpTime& op ){
_lastOp = op;
}
2010-04-02 17:29:33 +02:00
OpTime getLastOp() const {
return _lastOp;
}
2010-03-30 21:20:22 +02:00
void appendLastOp( BSONObjBuilder& b ){
if ( ! _lastOp.isNull() )
b.appendTimestamp( "lastOp" , _lastOp.asDate() );
}
2009-10-13 22:01:02 +02:00
/* each thread which does db operations has a Client object in TLS.
call this when your thread starts.
*/
static void initThread(const char *desc);
/*
this has to be called as the client goes away, but before thread termination
@return true if anything was done
*/
bool shutdown();
2009-10-23 17:13:08 +02:00
bool isGod() const { return _god; }
2010-02-04 21:56:02 +01:00
friend class CurOp;
2010-02-05 04:51:57 +01:00
string toString() const;
2010-04-02 17:29:33 +02:00
void gotHandshake( const BSONObj& o );
BSONObj getRemoteID() const { return _remoteId; }
BSONObj getHandshake() const { return _handshake; }
};
2009-10-13 22:01:02 +02:00
inline Client& cc() {
return *currentClient.get();
}
/* each thread which does db operations has a Client object in TLS.
call this when your thread starts.
*/
inline void Client::initThread(const char *desc) {
assert( currentClient.get() == 0 );
currentClient.reset( new Client(desc) );
}
2009-10-23 17:13:08 +02:00
inline Client::GodScope::GodScope(){
_prev = cc()._god;
cc()._god = true;
}
inline Client::GodScope::~GodScope(){
cc()._god = _prev;
}
2009-12-03 17:50:09 +01:00
/* this unlocks, does NOT upgrade. that works for our current usage */
2009-12-03 17:50:09 +01:00
inline void mongolock::releaseAndWriteLock() {
2009-12-06 16:06:02 +01:00
if( !_writelock ) {
#if BOOST_VERSION >= 103500
2009-12-06 16:06:02 +01:00
int s = dbMutex.getState();
if( s != -1 ) {
log() << "error: releaseAndWriteLock() s == " << s << endl;
msgasserted( 12600, "releaseAndWriteLock: unlock_shared failed, probably recursive" );
2009-12-06 16:06:02 +01:00
}
#endif
2009-12-03 17:50:09 +01:00
_writelock = true;
dbMutex.unlock_shared();
dbMutex.lock();
2010-01-30 04:00:50 +01:00
if ( cc().getContext() )
cc().getContext()->unlocked();
2009-12-03 17:50:09 +01:00
}
}
2010-02-05 04:51:57 +01:00
string sayClientState();
inline bool haveClient(){
return currentClient.get() > 0;
}
};
2009-10-23 17:13:08 +02:00