0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-30 17:10:48 +01:00

caching js context, also THREADSAFE mangling

This commit is contained in:
Eliot Horowitz 2009-08-05 13:14:06 -04:00
parent e1a52487e9
commit c9ad39db79
5 changed files with 229 additions and 21 deletions

View File

@ -59,8 +59,7 @@ namespace mongo {
return false;
}
auto_ptr<Scope> s( globalScriptEngine->createScope() );
auto_ptr<Scope> s = globalScriptEngine->getPooledScope( ns );
ScriptingFunction f = s->createFunction(code);
if ( f == 0 ) {
errmsg = (string)"compile failed: " + s->getError();

View File

@ -79,26 +79,22 @@ namespace mongo {
class Where {
public:
Where() {
scope = 0;
jsScope = 0;
}
~Where() {
if ( scope )
delete scope;
if ( jsScope )
if ( jsScope ){
delete jsScope;
scope = 0;
}
func = 0;
}
Scope * scope;
auto_ptr<Scope> scope;
ScriptingFunction func;
BSONObj *jsScope;
void setFunc(const char *code) {
massert( "scope has to be created first!" , scope );
massert( "scope has to be created first!" , scope.get() );
func = scope->createFunction( code );
}
@ -146,7 +142,8 @@ namespace mongo {
where = new Where();
uassert( "$where query, but no script engine", globalScriptEngine );
where->scope = globalScriptEngine->createScope();
assert( curNs );
where->scope = globalScriptEngine->getPooledScope( curNs );
where->scope->localConnect( database->name.c_str() );
if ( e.type() == CodeWScope ) {

View File

@ -45,8 +45,154 @@ namespace mongo {
return exec( data , filename , printResult , reportError , assertOnError, timeoutMs );
}
typedef map< string , list<Scope*> > PoolToScopes;
class ScopeCache {
public:
~ScopeCache(){
for ( PoolToScopes::iterator i=_pools.begin() ; i != _pools.end(); i++ ){
for ( list<Scope*>::iterator j=i->second.begin(); j != i->second.end(); j++ )
delete *j;
}
}
void done( const string& pool , Scope * s ){
boostlock lk( _mutex );
list<Scope*> & l = _pools[pool];
if ( l.size() > 10 ){
delete s;
}
else {
l.push_back( s );
s->reset();
}
}
Scope * get( const string& pool ){
boostlock lk( _mutex );
list<Scope*> & l = _pools[pool];
if ( l.size() == 0 )
return 0;
Scope * s = l.back();
l.pop_back();
s->reset();
return s;
}
private:
PoolToScopes _pools;
mutex _mutex;
};
thread_specific_ptr<ScopeCache> scopeCache;
class PooledScope : public Scope {
public:
PooledScope( const string pool , Scope * real ) : _pool( pool ) , _real( real ){};
virtual ~PooledScope(){
scopeCache->done( _pool , _real );
_real = 0;
}
void reset(){
_real->reset();
}
void init( BSONObj * data ){
_real->init( data );
}
void localConnect( const char * dbName ){
_real->localConnect( dbName );
}
void externalSetup(){
_real->externalSetup();
}
double getNumber( const char *field ){
return _real->getNumber( field );
}
string getString( const char *field ){
return _real->getString( field );
}
bool getBoolean( const char *field ){
return _real->getBoolean( field );
}
BSONObj getObject( const char *field ){
return _real->getObject( field );
}
int type( const char *field ){
return _real->type( field );
}
void setNumber( const char *field , double val ){
_real->setNumber( field , val );
}
void setString( const char *field , const char * val ){
_real->setString( field , val );
}
void setObject( const char *field , const BSONObj& obj , bool readOnly=true ){
_real->setObject( field , obj , readOnly );
}
void setBoolean( const char *field , bool val ){
_real->setBoolean( field , val );
}
void setThis( const BSONObj * obj ){
_real->setThis( obj );
}
ScriptingFunction createFunction( const char * code ){
return _real->createFunction( code );
}
/**
* @return 0 on success
*/
int invoke( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 ){
return _real->invoke( func , args , timeoutMs );
}
string getError(){
return _real->getError();
}
bool exec( const string& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ){
return _real->exec( code , name , printResult , reportError , assertOnError , timeoutMs );
}
bool execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ){
return _real->execFile( filename , printResult , reportError , assertOnError , timeoutMs );
}
void injectNative( const char *field, NativeFunction func ){
_real->injectNative( field , func );
}
void gc(){
_real->gc();
}
private:
string _pool;
Scope * _real;
};
auto_ptr<Scope> ScriptEngine::getPooledScope( const string& pool ){
if ( ! scopeCache.get() ){
scopeCache.reset( new ScopeCache() );
}
Scope * s = scopeCache->get( pool );
if ( ! s ){
s = createScope();
}
auto_ptr<Scope> p;
p.reset( new PooledScope( pool , s ) );
return p;
}
ScriptEngine * globalScriptEngine;
}

View File

@ -76,6 +76,8 @@ namespace mongo {
virtual bool utf8Ok() const = 0;
static void setup();
auto_ptr<Scope> getPooledScope( const string& pool );
};
extern ScriptEngine * globalScriptEngine;

View File

@ -26,9 +26,13 @@ namespace mongo {
_modified = false;
_magic = 17;
}
~BSONHolder(){
_magic = 18;
}
void check(){
uassert( "holder magic value is wrong" , _magic == 17 );
uassert( "holder magic value is wrong" , _magic == 17 && _obj.isValid() );
}
BSONFieldIterator * it();
@ -745,8 +749,14 @@ namespace mongo {
JS_SetCStringsAreUTF8();
#endif
#ifdef JS_THREADSAFE
_runtime = JS_NewRuntime(8L * 1024L * 1024L);
uassert( "JS_NewRuntime failed" , _runtime );
#else
#warning spider monkey compiled without THREADSAFE will be slower
cerr << "*** warning: spider monkey compiled without THREADSAFE will be slower" << endl;
#endif
if ( ! utf8Ok() ){
cerr << "*** warning: spider monkey build without utf8 support. consider rebuilding with utf8 support" << endl;
@ -754,7 +764,9 @@ namespace mongo {
}
~SMEngine(){
#ifdef JS_THREADSAFE
JS_DestroyRuntime( _runtime );
#endif
JS_ShutDown();
}
@ -765,7 +777,9 @@ namespace mongo {
virtual bool utf8Ok() const { return JS_CStringsAreUTF8(); }
private:
#ifdef JS_THREADSAFE
JSRuntime * _runtime;
#endif
friend class SMScope;
};
@ -808,10 +822,20 @@ namespace mongo {
return JS_FALSE;
}
JSBool yes_gc(JSContext *cx, JSGCStatus status){
return JS_TRUE;
}
class SMScope : public Scope {
public:
SMScope(){
#ifdef JS_THREADSAFE
_context = JS_NewContext( globalSMEngine->_runtime , 8192 );
#else
_runtime = JS_NewRuntime( 512L * 1024L );
massert( "JS_NewRuntime failed" , _runtime );
_context = JS_NewContext( _runtime , 8192 );
#endif
_convertor = new Convertor( _context );
massert( "JS_NewContext failed" , _context );
@ -834,34 +858,51 @@ namespace mongo {
"keySet" , object_keyset , 0 , JSPROP_READONLY ) );
_this = 0;
_externalSetup = false;
_localConnect = false;
//JS_SetGCCallback( _context , no_gc ); // this is useful for seeing if something is a gc problem
}
~SMScope(){
uassert( "deleted SMScope twice?" , _convertor );
for ( list<void*>::iterator i=_roots.begin(); i != _roots.end(); i++ ){
JS_RemoveRoot( _context , *i );
}
if ( _this )
_roots.clear();
if ( _this ){
JS_RemoveRoot( _context , &_this );
_this = 0;
}
if ( _convertor ){
delete _convertor;
_convertor = 0;
}
if ( _context ){
JS_DestroyContext( _context );
_context = 0;
}
}
#ifndef JS_THREADSAFE
JS_DestroyRuntime( _runtime );
_runtime = 0;
#endif
}
void reset(){
massert( "SMScope::reset() not implemented yet" , 0 );
assert( _convertor );
return;
if ( _this ){
JS_RemoveRoot( _context , &_this );
_this = 0;
}
currentScope.reset( this );
_error = "";
}
void addRoot( void * root , const char * name ){
JS_AddNamedRoot( _context , root , name );
_roots.push_back( root );
@ -875,19 +916,33 @@ namespace mongo {
while ( i.more() ){
BSONElement e = i.next();
_convertor->setProperty( _global , e.fieldName() , _convertor->toval( e ) );
_initFieldNames.insert( e.fieldName() );
}
}
void externalSetup(){
uassert( "already local connected" , ! _localConnect );
if ( _externalSetup )
return;
initMongoJS( this , _context , _global , false );
_externalSetup = true;
}
void localConnect( const char * dbName ){
uassert( "already setup for external db" , ! _externalSetup );
if ( _localConnect ){
uassert( "connected to different db" , _dbName == dbName );
return;
}
initMongoJS( this , _context , _global , true );
exec( "_mongo = new Mongo();" );
exec( ((string)"db = _mongo.getDB( \"" + dbName + "\" ); ").c_str() );
_localConnect = true;
_dbName = dbName;
}
// ----- getters ------
@ -968,7 +1023,7 @@ namespace mongo {
void setThis( const BSONObj * obj ){
if ( _this )
JS_RemoveRoot( _context , &_this );
_this = _convertor->toJSObject( obj );
JS_AddNamedRoot( _context , &_this , "scope this" );
@ -1104,6 +1159,9 @@ namespace mongo {
JSContext *context() const { return _context; }
private:
#ifndef JS_THREADSAFE
JSRuntime * _runtime;
#endif
JSContext * _context;
Convertor * _convertor;
@ -1112,6 +1170,12 @@ namespace mongo {
string _error;
list<void*> _roots;
bool _externalSetup;
bool _localConnect;
string _dbName;
set<string> _initFieldNames;
};
void errorReporter( JSContext *cx, const char *message, JSErrorReport *report ){