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:
parent
e1a52487e9
commit
c9ad39db79
@ -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();
|
||||
|
@ -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 ) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -76,6 +76,8 @@ namespace mongo {
|
||||
virtual bool utf8Ok() const = 0;
|
||||
|
||||
static void setup();
|
||||
|
||||
auto_ptr<Scope> getPooledScope( const string& pool );
|
||||
};
|
||||
|
||||
extern ScriptEngine * globalScriptEngine;
|
||||
|
@ -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 ){
|
||||
|
Loading…
Reference in New Issue
Block a user