diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index e4d49635ab..0fe3db6144 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -1001,6 +1001,7 @@ int ArangoServer::runUnitTests (TRI_vocbase_t* vocbase) { cout << TRI_StringifyV8Exception(&tryCatch); } else { + // will stop, so need for v8g->_canceled = true; return EXIT_FAILURE; } } @@ -1065,6 +1066,7 @@ int ArangoServer::runScript (TRI_vocbase_t* vocbase) { TRI_LogV8Exception(&tryCatch); } else { + // will stop, so need for v8g->_canceled = true; return EXIT_FAILURE; } } diff --git a/arangod/V8Server/ApplicationV8.cpp b/arangod/V8Server/ApplicationV8.cpp index 648ac0ec4d..be67af548d 100644 --- a/arangod/V8Server/ApplicationV8.cpp +++ b/arangod/V8Server/ApplicationV8.cpp @@ -148,7 +148,11 @@ namespace { } // ----------------------------------------------------------------------------- -// --SECTION-- public types +// --SECTION-- class V8Context +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- public methods // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// @@ -208,6 +212,21 @@ void ApplicationV8::V8Context::handleGlobalContextMethods () { } } +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the cancelation cleanup +//////////////////////////////////////////////////////////////////////////////// + +void ApplicationV8::V8Context::handleCancelationCleanup () { + v8::HandleScope scope; + + LOG_DEBUG("executing cancelation cleanup context %d", (int) _id); + + TRI_ExecuteJavaScriptString(_context, + v8::String::New("require('internal').cleanupCancelation();"), + v8::String::New("context cleanup method"), + false); +} + // ----------------------------------------------------------------------------- // --SECTION-- class ApplicationV8 // ----------------------------------------------------------------------------- @@ -360,10 +379,25 @@ void ApplicationV8::exitContext (V8Context* context) { // HasOutOfMemoryException must be called while there is still an isolate! bool const hasOutOfMemoryException = context->_context->HasOutOfMemoryException(); + // check for cancelation requests + bool const canceled = v8g->_canceled; + v8g->_canceled = false; + // exit the context context->_context->Exit(); context->_isolate->Exit(); + // if the execution was canceled, we need to cleanup + if (canceled) { + context->_isolate->Enter(); + context->_context->Enter(); + + context->handleCancelationCleanup(); + + context->_context->Exit(); + context->_isolate->Exit(); + } + // try to execute new global context methods bool runGlobal = false; { diff --git a/arangod/V8Server/ApplicationV8.h b/arangod/V8Server/ApplicationV8.h index 3bf2e0143f..c7321faca9 100644 --- a/arangod/V8Server/ApplicationV8.h +++ b/arangod/V8Server/ApplicationV8.h @@ -188,6 +188,12 @@ namespace triagens { void handleGlobalContextMethods (); +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the cancelation cleanup +//////////////////////////////////////////////////////////////////////////////// + + void handleCancelationCleanup (); + //////////////////////////////////////////////////////////////////////////////// /// @brief mutex to protect _globalMethods //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/V8Server/V8Job.cpp b/arangod/V8Server/V8Job.cpp index cd408f87c0..e3f6bbba9d 100644 --- a/arangod/V8Server/V8Job.cpp +++ b/arangod/V8Server/V8Job.cpp @@ -150,7 +150,10 @@ Job::status_t V8Job::work () { TRI_LogV8Exception(&tryCatch); } else { - LOG_WARNING("caught non-printable exception in periodic task"); + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; + LOG_WARNING("caught non-catchable exception (aka termination) in periodic job"); } } } diff --git a/arangod/V8Server/v8-actions.cpp b/arangod/V8Server/v8-actions.cpp index 197a33539f..c92553f164 100644 --- a/arangod/V8Server/v8-actions.cpp +++ b/arangod/V8Server/v8-actions.cpp @@ -730,6 +730,7 @@ static TRI_action_result_t ExecuteActionVocbase (TRI_vocbase_t* vocbase, } } else { + v8g->_canceled = true; result.isValid = false; result.canceled = true; } diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 4ca23a5c42..532ddb6f66 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -3499,6 +3499,9 @@ static v8::Handle ExecuteQueryCursorAhuacatl (TRI_vocbase_t* const vo return scope.Close(v8::ThrowException(tryCatch.Exception())); } else { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; return scope.Close(result); } } @@ -3847,6 +3850,9 @@ static v8::Handle JS_Transaction (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(tryCatch.Exception())); } else { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; return scope.Close(result); } } @@ -4489,6 +4495,9 @@ static v8::Handle JS_NextGeneralCursor (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(tryCatch.Exception())); } else { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; return scope.Close(v8::Undefined()); } } @@ -4566,6 +4575,9 @@ static v8::Handle JS_ToArrayGeneralCursor (v8::Arguments const& argv) return scope.Close(v8::ThrowException(tryCatch.Exception())); } else { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; return scope.Close(v8::Undefined()); } } @@ -5433,6 +5445,12 @@ static v8::Handle JS_RunAhuacatl (v8::Arguments const& argv) { TRI_ObjectToString(tryCatch.Exception()).c_str()); return scope.Close(v8::ThrowException(errorObject)); } + else { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; + return scope.Close(result); + } } return scope.Close(result); @@ -5562,6 +5580,9 @@ static v8::Handle JS_ExplainAhuacatl (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(errorObject)); } else { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; return scope.Close(result); } } @@ -5636,6 +5657,9 @@ static v8::Handle JS_ParseAhuacatl (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(errorObject)); } else { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; return scope.Close(result); } } diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js index 4c901c5a00..aa9ab615fa 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js @@ -2238,7 +2238,7 @@ var checkIfMayBeDropped = function(colName, graphName, graphs) { return; } var edgeDefinitions = graph.edgeDefinitions; - if (graph.edgeDefinitions) { + if (edgeDefinitions) { edgeDefinitions.forEach( function(edgeDefinition) { var from = edgeDefinition.from; @@ -2257,7 +2257,7 @@ var checkIfMayBeDropped = function(colName, graphName, graphs) { var orphanCollections = graph.orphanCollections; if (orphanCollections) { if (orphanCollections.indexOf(colName) !== -1) { - return false; + result = false; } } } diff --git a/js/common/bootstrap/modules.js b/js/common/bootstrap/modules.js index 7ac5d6127b..12e795367f 100644 --- a/js/common/bootstrap/modules.js +++ b/js/common/bootstrap/modules.js @@ -503,74 +503,80 @@ function require (path) { } // create a new module - var localModule = currentPackage.defineModule( - id, - "js", - new Module(id, currentPackage, currentModule._applicationContext, path, origin, false)); + try { + var localModule = currentPackage.defineModule( + id, + "js", + new Module(id, currentPackage, currentModule._applicationContext, path, origin, false)); - // create a new sandbox and execute - var env = currentPackage._environment; + // create a new sandbox and execute + var env = currentPackage._environment; - var sandbox = {}; - sandbox.print = internal.print; + var sandbox = {}; + sandbox.print = internal.print; - if (env !== undefined) { - for (key in env) { - if (env.hasOwnProperty(key) && key !== "__myenv__") { - sandbox[key] = env[key]; + if (env !== undefined) { + for (key in env) { + if (env.hasOwnProperty(key) && key !== "__myenv__") { + sandbox[key] = env[key]; + } } } - } - var filename = fileUri2Path(origin); + var filename = fileUri2Path(origin); - if (filename !== null) { - sandbox.__filename = filename; - sandbox.__dirname = normalizeModuleName(filename + "/.."); - } - - sandbox.module = localModule; - sandbox.exports = localModule.exports; - - sandbox.require = function(path) { - return localModule.require(path); - }; - - if (localModule.hasOwnProperty("_applicationContext")) { - sandbox.applicationContext = localModule._applicationContext; - } - - // try to execute the module source code - var script = "(function (__myenv__) {"; - - for (key in sandbox) { - if (sandbox.hasOwnProperty(key)) { - script += "var " + key + " = __myenv__['" + key + "'];"; + if (filename !== null) { + sandbox.__filename = filename; + sandbox.__dirname = normalizeModuleName(filename + "/.."); } + + sandbox.module = localModule; + sandbox.exports = localModule.exports; + + sandbox.require = function(path) { + return localModule.require(path); + }; + + if (localModule.hasOwnProperty("_applicationContext")) { + sandbox.applicationContext = localModule._applicationContext; + } + + // try to execute the module source code + var script = "(function (__myenv__) {"; + + for (key in sandbox) { + if (sandbox.hasOwnProperty(key)) { + script += "var " + key + " = __myenv__['" + key + "'];"; + } + } + + script += "delete __myenv__;" + + content + + "\n});"; + + var fun = internal.executeScript(script, undefined, filename); + + if (fun === undefined) { + e = new Error("corrupted package '" + path + + "', cannot create module context function for: " + + script); + + e.moduleNotFound = false; + e._path = path; + e._package = currentPackage.id; + e._packageOrigin = currentPackage._origin; + + throw e; + } + + fun(sandbox); + + return localModule; } - - script += "delete __myenv__;" - + content - + "\n});"; - - var fun = internal.executeScript(script, undefined, filename); - - if (fun === undefined) { - e = new Error("corrupted package '" + path - + "', cannot create module context function for: " - + script); - - e.moduleNotFound = false; - e._path = path; - e._package = currentPackage.id; - e._packageOrigin = currentPackage._origin; - - throw e; + catch (err) { + currentPackage.clearModule(id, "js"); + throw err; } - - fun(sandbox); - - return localModule; } //////////////////////////////////////////////////////////////////////////////// @@ -903,6 +909,14 @@ function require (path) { delete REGISTER_EXECUTE_FILE; +//////////////////////////////////////////////////////////////////////////////// +/// @brief cleans up after cancelation +//////////////////////////////////////////////////////////////////////////////// + + function cleanupCancelation () { + module.unloadAll(); + } + // ----------------------------------------------------------------------------- // --SECTION-- Package // ----------------------------------------------------------------------------- @@ -1106,6 +1120,8 @@ function require (path) { internal[key] = EXPORTS_SLOW_BUFFER[key]; } } + + internal.cleanupCancelation = cleanupCancelation; }()); delete EXPORTS_SLOW_BUFFER; diff --git a/lib/V8/JSLoader.cpp b/lib/V8/JSLoader.cpp index cc62bebf34..31ec4e4c6c 100644 --- a/lib/V8/JSLoader.cpp +++ b/lib/V8/JSLoader.cpp @@ -85,6 +85,9 @@ v8::Handle JSLoader::executeGlobalScript (v8::Handle con return scope.Close(v8::Undefined()); } else { + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + + v8g->_canceled = true; return scope.Close(result); } } @@ -121,6 +124,9 @@ bool JSLoader::loadScript (v8::Persistent context, string const& na return false; } else { + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + + v8g->_canceled = true; return false; } } diff --git a/lib/V8/v8-execution.cpp b/lib/V8/v8-execution.cpp index a0e76fbffb..02c0bc6a78 100644 --- a/lib/V8/v8-execution.cpp +++ b/lib/V8/v8-execution.cpp @@ -74,6 +74,9 @@ TRI_js_exec_context_t* TRI_CreateExecutionContext (char const* script, ctx->_error = TRI_ERROR_INTERNAL; } else { + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + + v8g->_canceled = true; ctx->_error = TRI_ERROR_REQUEST_CANCELED; } return ctx; @@ -134,6 +137,9 @@ TRI_json_t* TRI_ExecuteResultContext (TRI_js_exec_context_t* ctx) { ctx->_error = TRI_ERROR_INTERNAL; } else { + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + + v8g->_canceled = true; ctx->_error = TRI_ERROR_REQUEST_CANCELED; } return NULL; diff --git a/lib/V8/v8-globals.cpp b/lib/V8/v8-globals.cpp index 0f27f95e2e..e894903bfa 100644 --- a/lib/V8/v8-globals.cpp +++ b/lib/V8/v8-globals.cpp @@ -128,9 +128,10 @@ TRI_v8_global_s::TRI_v8_global_s (v8::Isolate* isolate) _resolver(0), _server(0), _vocbase(0), - _loader(0), _allowUseDatabase(true), - _hasDeadObjects(false) { + _hasDeadObjects(false), + _loader(0), + _canceled(false) { v8::HandleScope scope; BufferConstant = v8::Persistent::New(isolate, TRI_V8_SYMBOL("Buffer")); diff --git a/lib/V8/v8-globals.h b/lib/V8/v8-globals.h index 9aec7c1586..9f97cd20ec 100644 --- a/lib/V8/v8-globals.h +++ b/lib/V8/v8-globals.h @@ -746,12 +746,6 @@ typedef struct TRI_v8_global_s { void* _vocbase; -//////////////////////////////////////////////////////////////////////////////// -/// @brief pointer to the startup loader (JSLoader*) -//////////////////////////////////////////////////////////////////////////////// - - void* _loader; - //////////////////////////////////////////////////////////////////////////////// /// @brief whether or not useDatabase() is allowed //////////////////////////////////////////////////////////////////////////////// @@ -764,6 +758,23 @@ typedef struct TRI_v8_global_s { //////////////////////////////////////////////////////////////////////////////// bool _hasDeadObjects; + +// ----------------------------------------------------------------------------- +// --SECTION-- GENERAL +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief pointer to the startup loader (JSLoader*) +//////////////////////////////////////////////////////////////////////////////// + + void* _loader; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief cancel has been caught +//////////////////////////////////////////////////////////////////////////////// + + bool _canceled; + } TRI_v8_global_t; diff --git a/lib/V8/v8-utils.cpp b/lib/V8/v8-utils.cpp index 41eac86bfa..ead1b64480 100644 --- a/lib/V8/v8-utils.cpp +++ b/lib/V8/v8-utils.cpp @@ -267,6 +267,11 @@ static bool LoadJavaScriptDirectory (char const* path, if (tryCatch.CanContinue()) { TRI_LogV8Exception(&tryCatch); } + else { + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + + v8g->_canceled = true; + } } } @@ -418,6 +423,9 @@ static v8::Handle JS_Parse (v8::Arguments const& argv) { TRI_V8_SYNTAX_ERROR(scope, err.c_str()); } else { + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + + v8g->_canceled = true; return scope.Close(v8::Undefined()); } } @@ -828,6 +836,9 @@ static v8::Handle JS_Execute (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(tryCatch.Exception())); } else { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; return scope.Close(v8::Undefined()); } } @@ -846,6 +857,9 @@ static v8::Handle JS_Execute (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(tryCatch.Exception())); } else { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; return scope.Close(v8::Undefined()); } } @@ -3300,6 +3314,9 @@ v8::Handle TRI_ExecuteJavaScriptString (v8::Handle conte TRI_LogV8Exception(&tryCatch); } else { + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + + v8g->_canceled = true; return scope.Close(v8::Undefined()); } }