1
0
Fork 0
arangodb/arangod/V8Server/V8Context.cpp

206 lines
5.9 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
////////////////////////////////////////////////////////////////////////////////
#include "V8Context.h"
#include "Basics/MutexLocker.h"
#include "Logger/Logger.h"
#include "V8/v8-globals.h"
#include "V8/v8-utils.h"
using namespace arangodb;
using namespace arangodb::basics;
std::string const GlobalContextMethods::CodeReloadRouting =
"require(\"@arangodb/actions\").reloadRouting();";
std::string const GlobalContextMethods::CodeReloadAql =
"try { require(\"@arangodb/aql\").reload(); } catch (err) { }";
V8Context::V8Context(size_t id, v8::Isolate* isolate)
: _id(id),
_isolate(isolate),
_locker(nullptr),
_creationStamp(TRI_microtime()),
_lastGcStamp(0.0),
_invocations(0),
_invocationsSinceLastGc(0),
_hasActiveExternals(false) {}
void V8Context::lockAndEnter() {
TRI_ASSERT(_isolate != nullptr);
TRI_ASSERT(_locker == nullptr);
_locker = new v8::Locker(_isolate);
_isolate->Enter();
assertLocked();
++_invocations;
++_invocationsSinceLastGc;
}
void V8Context::unlockAndExit() {
assertLocked();
_isolate->Exit();
delete _locker;
_locker = nullptr;
TRI_ASSERT(!v8::Locker::IsLocked(_isolate));
}
void V8Context::assertLocked() const {
TRI_ASSERT(_locker != nullptr);
TRI_ASSERT(_isolate != nullptr);
TRI_ASSERT(_locker->IsLocked(_isolate));
TRI_ASSERT(v8::Locker::IsLocked(_isolate));
}
bool V8Context::hasGlobalMethodsQueued() {
MUTEX_LOCKER(mutexLocker, _globalMethodsLock);
return !_globalMethods.empty();
}
void V8Context::setCleaned(double stamp) {
_lastGcStamp = stamp;
_invocationsSinceLastGc = 0;
}
double V8Context::age() const { return TRI_microtime() - _creationStamp; }
bool V8Context::shouldBeRemoved(double maxAge, uint64_t maxInvocations) const {
if (maxAge > 0.0 && age() > maxAge) {
// context is "too old"
return true;
}
if (maxInvocations > 0 && _invocations >= maxInvocations) {
// context is used often enough
return true;
}
// re-use the context
return false;
}
bool V8Context::addGlobalContextMethod(std::string const& method) {
GlobalContextMethods::MethodType type = GlobalContextMethods::type(method);
if (type == GlobalContextMethods::MethodType::UNKNOWN) {
return false;
}
MUTEX_LOCKER(mutexLocker, _globalMethodsLock);
for (auto& it : _globalMethods) {
if (it == type) {
// action is already registered. no need to register it again
return true;
}
}
// insert action into vector
_globalMethods.emplace_back(type);
return true;
}
void V8Context::handleGlobalContextMethods() {
std::vector<GlobalContextMethods::MethodType> copy;
try {
// we need to copy the vector of functions so we do not need to hold the
// lock while we execute them
// this avoids potential deadlocks when one of the executed functions itself
// registers a context method
MUTEX_LOCKER(mutexLocker, _globalMethodsLock);
copy.swap(_globalMethods);
} catch (...) {
// if we failed, we shouldn't have modified _globalMethods yet, so we can
// try again on the next invocation
return;
}
for (auto& type : copy) {
std::string const& func = GlobalContextMethods::code(type);
LOG_TOPIC("fcb75", DEBUG, arangodb::Logger::V8)
<< "executing global context method '" << func << "' for context " << _id;
TRI_GET_GLOBALS2(_isolate);
// save old security context settings
JavaScriptSecurityContext old(v8g->_securityContext);
v8g->_securityContext = JavaScriptSecurityContext::createInternalContext();
try {
v8::TryCatch tryCatch(_isolate);
TRI_ExecuteJavaScriptString(_isolate, _isolate->GetCurrentContext(),
TRI_V8_STD_STRING(_isolate, func),
TRI_V8_ASCII_STRING(_isolate,
"global context method"),
false);
if (tryCatch.HasCaught()) {
if (tryCatch.CanContinue()) {
TRI_LogV8Exception(_isolate, &tryCatch);
}
}
} catch (...) {
LOG_TOPIC("d0adc", WARN, arangodb::Logger::V8)
<< "caught exception during global context method '" << func << "'";
}
// restore old security settings
v8g->_securityContext = old;
}
}
void V8Context::handleCancelationCleanup() {
v8::HandleScope scope(_isolate);
LOG_TOPIC("e8060", DEBUG, arangodb::Logger::V8)
<< "executing cancelation cleanup context #" << _id;
try {
TRI_ExecuteJavaScriptString(
_isolate, _isolate->GetCurrentContext(),
TRI_V8_ASCII_STRING(_isolate,
"require('module')._cleanupCancelation();"),
TRI_V8_ASCII_STRING(_isolate, "context cleanup method"), false);
} catch (...) {
LOG_TOPIC("558dd", WARN, arangodb::Logger::V8)
<< "caught exception during cancelation cleanup";
// do not throw from here
}
}
V8ContextEntryGuard::V8ContextEntryGuard(V8Context* context)
: _context(context) {
_context->lockAndEnter();
}
V8ContextEntryGuard::~V8ContextEntryGuard() { _context->unlockAndExit(); }