/* clientcursor.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 .
*/
/* Cursor -- and its derived classes -- are our internal cursors.
ClientCursor is a wrapper that represents a cursorid from our database
application's perspective.
*/
#pragma once
#include "../stdafx.h"
#include "cursor.h"
#include "jsobj.h"
#include "../util/message.h"
#include "storage.h"
#include "dbhelpers.h"
#include "matcher.h"
namespace mongo {
typedef long long CursorId; /* passed to the client so it can send back on getMore */
class Cursor; /* internal server cursor base class */
class ClientCursor;
/* todo: make this map be per connection. this will prevent cursor hijacking security attacks perhaps.
*/
typedef map CCById;
typedef multimap CCByLoc;
extern BSONObj id_obj;
class ClientCursor {
friend class CmdCursorInfo;
DiskLoc _lastLoc; // use getter and setter not this (important)
unsigned _idleAgeMillis; // how long has the cursor been around, relative to server idle time
bool _liveForever; // if true, never time out cursor
static CCById clientCursorsById;
static CCByLoc byLoc;
static boost::recursive_mutex ccmutex; // must use this for all statics above!
static CursorId allocCursorId_inlock();
public:
/*const*/ CursorId cursorid;
string ns;
auto_ptr matcher;
auto_ptr c;
int pos; // # objects into the cursor so far
BSONObj query;
ClientCursor() : _idleAgeMillis(0), _liveForever(false), pos(0) {
recursive_boostlock lock(ccmutex);
cursorid = allocCursorId_inlock();
clientCursorsById.insert( make_pair(cursorid, this) );
}
~ClientCursor();
DiskLoc lastLoc() const {
return _lastLoc;
}
auto_ptr< FieldMatcher > filter; // which fields query wants returned
Message originalMessage; // this is effectively an auto ptr for data the matcher points to
/* Get rid of cursors for namespaces that begin with nsprefix.
Used by drop, deleteIndexes, dropDatabase.
*/
static void invalidate(const char *nsPrefix);
private:
void setLastLoc_inlock(DiskLoc);
static ClientCursor* find_inlock(CursorId id, bool warn = true) {
CCById::iterator it = clientCursorsById.find(id);
if ( it == clientCursorsById.end() ) {
if ( warn )
OCCASIONALLY out() << "ClientCursor::find(): cursor not found in map " << id << " (ok after a drop)\n";
return 0;
}
return it->second;
}
public:
static ClientCursor* find(CursorId id, bool warn = true) {
recursive_boostlock lock(ccmutex);
return find_inlock(id, warn);
}
static bool erase(CursorId id) {
recursive_boostlock lock(ccmutex);
ClientCursor *cc = find_inlock(id);
if ( cc ) {
delete cc;
return true;
}
return false;
}
/* call when cursor's location changes so that we can update the
cursorsbylocation map. if you are locked and internally iterating, only
need to call when you are ready to "unlock".
*/
void updateLocation();
void cleanupByLocation(DiskLoc loc);
void mayUpgradeStorage() {
/* if ( !ids_.get() )
return;
stringstream ss;
ss << ns << "." << cursorid;
ids_->mayUpgradeStorage( ss.str() );*/
}
/**
* @param millis amount of idle passed time since last call
*/
bool shouldTimeout( unsigned millis ){
_idleAgeMillis += millis;
return ! _liveForever && _idleAgeMillis > 600000;
}
unsigned idleTime(){
return _idleAgeMillis;
}
static void idleTimeReport(unsigned millis);
void liveForever() {
_liveForever = true;
}
static unsigned byLocSize(); // just for diagnostics
// static void idleTimeReport(unsigned millis);
static void informAboutToDeleteBucket(const DiskLoc& b);
static void aboutToDelete(const DiskLoc& dl);
};
} // namespace mongo