summaryrefslogtreecommitdiff
path: root/src/mongo/scripting/engine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/scripting/engine.cpp')
-rw-r--r--src/mongo/scripting/engine.cpp191
1 files changed, 108 insertions, 83 deletions
diff --git a/src/mongo/scripting/engine.cpp b/src/mongo/scripting/engine.cpp
index fc4d042c2af..6f24e26c2c0 100644
--- a/src/mongo/scripting/engine.cpp
+++ b/src/mongo/scripting/engine.cpp
@@ -13,6 +13,18 @@
*
* 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/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
*/
#include "mongo/pch.h"
@@ -24,12 +36,15 @@
#include "mongo/client/dbclientcursor.h"
#include "mongo/client/dbclientinterface.h"
+#include "mongo/platform/unordered_set.h"
#include "mongo/scripting/bench.h"
#include "mongo/util/file.h"
+#include "mongo/util/text.h"
namespace mongo {
long long Scope::_lastVersion = 1;
static const unsigned kMaxJsFileLength = std::numeric_limits<unsigned>::max() - 1;
+ DBClientBase* directDBClient;
ScriptEngine::ScriptEngine() : _scopeInitCallback() {
}
@@ -39,7 +54,7 @@ namespace mongo {
Scope::Scope() : _localDBName(""),
_loadedVersion(0),
- _numTimeUsed(0),
+ _numTimesUsed(0),
_lastRetIsNativeCode(false) {
}
@@ -94,8 +109,11 @@ namespace mongo {
bool Scope::execFile(const string& filename, bool printResult, bool reportError,
int timeoutMs) {
-
+#ifdef _WIN32
+ boost::filesystem::path p(toWideString(filename.c_str()));
+#else
boost::filesystem::path p(filename);
+#endif
if (!exists(p)) {
log() << "file [" << filename << "] doesn't exist" << endl;
return false;
@@ -111,7 +129,7 @@ namespace mongo {
boost::filesystem::path sub(*it);
if (!endsWith(sub.string().c_str(), ".js"))
continue;
- if (!execFile(sub.string().c_str(), printResult, reportError, timeoutMs))
+ if (!execFile(sub.string(), printResult, reportError, timeoutMs))
return false;
}
@@ -125,6 +143,10 @@ namespace mongo {
File f;
f.open(filename.c_str(), true);
+
+ if (!f.is_open() || f.bad())
+ return false;
+
fileofs fo = f.len();
if (fo > kMaxJsFileLength) {
warning() << "attempted to execute javascript file larger than 2GB" << endl;
@@ -170,8 +192,8 @@ namespace mongo {
_loadedVersion = _lastVersion;
string coll = _localDBName + ".system.js";
- static DBClientBase* db = createDirectClient();
- auto_ptr<DBClientCursor> c = db->query(coll, Query(), 0, 0, NULL, QueryOption_SlaveOk, 0);
+ auto_ptr<DBClientCursor> c = directDBClient->query(coll, Query(), 0, 0, NULL,
+ QueryOption_SlaveOk, 0);
massert(16669, "unable to get db client cursor from query", c.get());
set<string> thisTime;
@@ -237,8 +259,10 @@ namespace mongo {
extern const JSFile mongo;
extern const JSFile mr;
extern const JSFile query;
+ extern const JSFile upgrade_check;
extern const JSFile utils;
extern const JSFile utils_sh;
+ extern const JSFile bulk_api;
}
void Scope::execCoreFiles() {
@@ -248,7 +272,9 @@ namespace mongo {
execSetup(JSFiles::mongo);
execSetup(JSFiles::mr);
execSetup(JSFiles::query);
+ execSetup(JSFiles::bulk_api);
execSetup(JSFiles::collection);
+ execSetup(JSFiles::upgrade_check);
}
/** install BenchRunner suite */
@@ -259,90 +285,80 @@ namespace mongo {
injectNative("benchFinish", BenchRunner::benchFinish);
}
- typedef map<string, list<Scope*> > PoolToScopes;
-
+namespace {
class ScopeCache {
public:
- ScopeCache() : _mutex("ScopeCache") {
- }
+ ScopeCache() : _mutex("ScopeCache") {}
- ~ScopeCache() {
- if (inShutdown())
- return;
- clear();
- }
-
- void done(const string& pool, Scope* s) {
+ void release(const string& poolName, const boost::shared_ptr<Scope>& scope) {
scoped_lock lk(_mutex);
- list<Scope*>& l = _pools[pool];
- bool oom = s->hasOutOfMemoryException();
- // do not keep too many contexts, or use them for too long
- if (l.size() > 10 || s->getTimeUsed() > 10 || oom || !s->getError().empty()) {
- delete s;
- }
- else {
- l.push_back(s);
- s->reset();
+ if (scope->hasOutOfMemoryException()) {
+ // make some room
+ log() << "Clearing all idle JS contexts due to out of memory" << endl;
+ _pools.clear();
+ return;
}
- if (oom) {
- // out of mem, make some room
- log() << "Clearing all idle JS contexts due to out of memory" << endl;
- clear();
+ if (scope->getTimesUsed() > kMaxScopeReuse)
+ return; // used too many times to save
+
+ if (!scope->getError().empty())
+ return; // not saving errored scopes
+
+ if (_pools.size() >= kMaxPoolSize) {
+ // prefer to keep recently-used scopes
+ _pools.pop_back();
}
+
+ ScopeAndPool toStore = {scope, poolName};
+ _pools.push_front(toStore);
}
- Scope* get(const string& pool) {
+ boost::shared_ptr<Scope> tryAcquire(const string& poolName) {
scoped_lock lk(_mutex);
- list<Scope*>& l = _pools[pool];
- if (l.size() == 0)
- return 0;
-
- Scope* s = l.back();
- l.pop_back();
- s->reset();
- s->incTimeUsed();
- return s;
- }
- void clear() {
- set<Scope*> seen;
- for (PoolToScopes::iterator i = _pools.begin(); i != _pools.end(); ++i) {
- for (list<Scope*>::iterator j = i->second.begin(); j != i->second.end(); ++j) {
- Scope* s = *j;
- fassert(16652, seen.insert(s).second);
- delete s;
+ for (Pools::iterator it = _pools.begin(); it != _pools.end(); ++it) {
+ if (it->poolName == poolName) {
+ boost::shared_ptr<Scope> scope = it->scope;
+ _pools.erase(it);
+ scope->incTimesUsed();
+ scope->reset();
+ return scope;
}
}
- _pools.clear();
+
+ return boost::shared_ptr<Scope>();
}
private:
- PoolToScopes _pools;
+ struct ScopeAndPool {
+ boost::shared_ptr<Scope> scope;
+ string poolName;
+ };
+
+ // Note: if these numbers change, reconsider choice of datastructure for _pools
+ static const unsigned kMaxPoolSize = 10;
+ static const int kMaxScopeReuse = 10;
+
+ typedef deque<ScopeAndPool> Pools; // More-recently used Scopes are kept at the front.
+ Pools _pools; // protected by _mutex
mongo::mutex _mutex;
};
- thread_specific_ptr<ScopeCache> scopeCache;
+ ScopeCache scopeCache;
+} // anonymous namespace
class PooledScope : public Scope {
public:
- PooledScope(const std::string& pool, Scope* real) : _pool(pool), _real(real) {
+ PooledScope(const std::string& pool, const boost::shared_ptr<Scope>& real)
+ : _pool(pool)
+ , _real(real) {
_real->loadStored(true);
- };
+ }
+
virtual ~PooledScope() {
- ScopeCache* sc = scopeCache.get();
- if (sc) {
- sc->done(_pool, _real);
- _real = NULL;
- }
- else {
- // this means that the Scope was killed from a different thread
- // for example a cursor got timed out that has a $where clause
- LOG(3) << "warning: scopeCache is empty!" << endl;
- delete _real;
- _real = 0;
- }
+ scopeCache.release(_pool, _real);
}
// wrappers for the derived (_real) scope
@@ -363,7 +379,7 @@ namespace mongo {
bool getBoolean(const char* field) { return _real->getBoolean(field); }
BSONObj getObject(const char* field) { return _real->getObject(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 setString(const char* field, const StringData& val) { _real->setString(field, val); }
void setElement(const char* field, const BSONElement& val) {
_real->setElement(field, val);
}
@@ -404,31 +420,24 @@ namespace mongo {
private:
string _pool;
- Scope* _real;
+ boost::shared_ptr<Scope> _real;
};
/** Get a scope from the pool of scopes matching the supplied pool name */
- auto_ptr<Scope> ScriptEngine::getPooledScope(const string& pool, const string& scopeType) {
- if (!scopeCache.get())
- scopeCache.reset(new ScopeCache());
-
- Scope* s = scopeCache->get(pool + scopeType);
- if (!s)
- s = newScope();
+ auto_ptr<Scope> ScriptEngine::getPooledScope(const string& db, const string& scopeType) {
+ const string fullPoolName = db + scopeType;
+ boost::shared_ptr<Scope> s = scopeCache.tryAcquire(fullPoolName);
+ if (!s) {
+ s.reset(newScope());
+ }
auto_ptr<Scope> p;
- p.reset(new PooledScope(pool + scopeType, s));
- p->setLocalDB(pool);
+ p.reset(new PooledScope(fullPoolName, s));
+ p->setLocalDB(db);
p->loadStored(true);
return p;
}
- void ScriptEngine::threadDone() {
- ScopeCache* sc = scopeCache.get();
- if (sc)
- sc->clear();
- }
-
void (*ScriptEngine::_connectCallback)(DBClientWithCommands&) = 0;
const char* (*ScriptEngine::_checkInterruptCallback)() = 0;
unsigned (*ScriptEngine::_getCurrentOpIdCallback)() = 0;
@@ -439,8 +448,24 @@ namespace mongo {
if (x == string::npos)
return false;
- return (x == 0 || !isalpha(code[x-1])) &&
- !isalpha(code[x+6]);
+ int quoteCount = 0;
+ int singleQuoteCount = 0;
+ for (size_t i = 0; i < x; i++) {
+ if (code[i] == '"') {
+ quoteCount++;
+ } else if(code[i] == '\'') {
+ singleQuoteCount++;
+ }
+ }
+ // if we are in either single quotes or double quotes return false
+ if (quoteCount % 2 != 0 || singleQuoteCount % 2 != 0) {
+ return false;
+ }
+
+ // return is at start OR preceded by space
+ // AND return is not followed by digit or letter
+ return (x == 0 || isspace(code[x-1])) &&
+ !(isalpha(code[x+6]) || isdigit(code[x+6]));
}
const char* jsSkipWhiteSpace(const char* raw) {