From e433a2831e2457f81c53b134c2bc7068b1e6b842 Mon Sep 17 00:00:00 2001 From: Willi Goesgens Date: Wed, 8 Oct 2014 16:48:30 +0200 Subject: [PATCH] Add a new wrapper which does fork + waitpid in one js call, so we're closer to the flame and less eventually miss something. --- .../frontend/js/bootstrap/module-internal.js | 19 ++- js/common/bootstrap/module-internal.js | 14 +- js/server/modules/org/arangodb/testing.js | 4 +- lib/V8/v8-utils.cpp | 144 ++++++++++++++++++ 4 files changed, 175 insertions(+), 6 deletions(-) diff --git a/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js b/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js index 841fd3eaa7..9385286a66 100644 --- a/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js +++ b/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js @@ -17,8 +17,9 @@ COLOR_BOLD_MAGENTA, PRETTY_PRINT, VALGRIND, VERSION, BYTES_SENT_DISTRIBUTION, BYTES_RECEIVED_DISTRIBUTION, CONNECTION_TIME_DISTRIBUTION, REQUEST_TIME_DISTRIBUTION, DEVELOPMENT_MODE, FE_DEVELOPMENT_MODE, THREAD_NUMBER, LOGFILE_PATH, - SYS_PLATFORM, SYS_EXECUTE_EXTERNAL, SYS_STATUS_EXTERNAL, SYS_KILL_EXTERNAL, - SYS_REGISTER_TASK, SYS_UNREGISTER_TASK, SYS_GET_TASK, SYS_TEST_PORT, SYS_IS_IP */ + SYS_PLATFORM, SYS_EXECUTE_EXTERNAL, SYS_STATUS_EXTERNAL, SYS_EXECUTE_EXTERNAL_AND_WAIT, + SYS_KILL_EXTERNAL, SYS_REGISTER_TASK, SYS_UNREGISTER_TASK, SYS_GET_TASK, SYS_TEST_PORT, + SYS_IS_IP */ //////////////////////////////////////////////////////////////////////////////// /// @brief module "internal" @@ -689,6 +690,20 @@ delete SYS_EXECUTE_EXTERNAL; } + if (typeof SYS_EXECUTE_EXTERNAL_AND_WAIT !== "undefined") { + exports.executeExternalAndWait = SYS_EXECUTE_EXTERNAL_AND_WAIT; + delete SYS_EXECUTE_EXTERNAL_AND_WAIT; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executeExternalAndWait - instantly waits for the exit, returns +/// joint result. +//////////////////////////////////////////////////////////////////////////////// + if (typeof SYS_EXECUTE_EXTERNAL_AND_WAIT !== "undefined") { + exports.executeExternalAndWait = SYS_EXECUTE_EXTERNAL_AND_WAIT; + delete SYS_EXECUTE_EXTERNAL_AND_WAIT; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief killExternal //////////////////////////////////////////////////////////////////////////////// diff --git a/js/common/bootstrap/module-internal.js b/js/common/bootstrap/module-internal.js index 841fd3eaa7..c70b0d3dde 100644 --- a/js/common/bootstrap/module-internal.js +++ b/js/common/bootstrap/module-internal.js @@ -17,8 +17,9 @@ COLOR_BOLD_MAGENTA, PRETTY_PRINT, VALGRIND, VERSION, BYTES_SENT_DISTRIBUTION, BYTES_RECEIVED_DISTRIBUTION, CONNECTION_TIME_DISTRIBUTION, REQUEST_TIME_DISTRIBUTION, DEVELOPMENT_MODE, FE_DEVELOPMENT_MODE, THREAD_NUMBER, LOGFILE_PATH, - SYS_PLATFORM, SYS_EXECUTE_EXTERNAL, SYS_STATUS_EXTERNAL, SYS_KILL_EXTERNAL, - SYS_REGISTER_TASK, SYS_UNREGISTER_TASK, SYS_GET_TASK, SYS_TEST_PORT, SYS_IS_IP */ + SYS_PLATFORM, SYS_EXECUTE_EXTERNAL, SYS_STATUS_EXTERNAL, SYS_EXECUTE_EXTERNAL_AND_WAIT, + SYS_KILL_EXTERNAL, SYS_REGISTER_TASK, SYS_UNREGISTER_TASK, SYS_GET_TASK, SYS_TEST_PORT, + SYS_IS_IP */ //////////////////////////////////////////////////////////////////////////////// /// @brief module "internal" @@ -689,6 +690,15 @@ delete SYS_EXECUTE_EXTERNAL; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief executeExternalAndWait - instantly waits for the exit, returns +/// joint result. +//////////////////////////////////////////////////////////////////////////////// + if (typeof SYS_EXECUTE_EXTERNAL_AND_WAIT !== "undefined") { + exports.executeExternalAndWait = SYS_EXECUTE_EXTERNAL_AND_WAIT; + delete SYS_EXECUTE_EXTERNAL_AND_WAIT; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief killExternal //////////////////////////////////////////////////////////////////////////////// diff --git a/js/server/modules/org/arangodb/testing.js b/js/server/modules/org/arangodb/testing.js index d5217f29d5..8eb9e79f26 100644 --- a/js/server/modules/org/arangodb/testing.js +++ b/js/server/modules/org/arangodb/testing.js @@ -93,6 +93,7 @@ var wait = require("internal").wait; var executeExternal = require("internal").executeExternal; var killExternal = require("internal").killExternal; var statusExternal = require("internal").statusExternal; +var executeExternalAndWait = require("internal").executeExternalAndWait; var base64Encode = require("internal").base64Encode; var PortFinder = require("org/arangodb/cluster").PortFinder; @@ -398,9 +399,8 @@ function runThere (options, instanceInfo, file) { } function executeAndWait (cmd, args) { - var pid = executeExternal(cmd, args); var startTime = time(); - var res = statusExternal(pid, true); + var res = executeExternalAndWait(cmd, args); var deltaTime = time() - startTime; if (res.status === "TERMINATED") { diff --git a/lib/V8/v8-utils.cpp b/lib/V8/v8-utils.cpp index fcae713886..836c13bcfe 100644 --- a/lib/V8/v8-utils.cpp +++ b/lib/V8/v8-utils.cpp @@ -3016,6 +3016,149 @@ static v8::Handle JS_StatusExternal (v8::Arguments const& argv) { return scope.Close(result); } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes a external program +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_ExecuteAndWaitExternal (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract the arguments + if (3 < argv.Length() || argv.Length() < 1) { + TRI_V8_EXCEPTION_USAGE(scope, + "executeAndWaitExternal([, [,] ])"); + } + + TRI_Utf8ValueNFC name(TRI_UNKNOWN_MEM_ZONE, argv[0]); + + if (*name == 0) { + TRI_V8_TYPE_ERROR(scope, " must be a string"); + } + + char** arguments = 0; + uint32_t n = 0; + + if (2 <= argv.Length()) { + v8::Handle a = argv[1]; + + if (a->IsArray()) { + v8::Handle arr = v8::Handle::Cast(a); + + n = arr->Length(); + arguments = (char**) TRI_Allocate(TRI_CORE_MEM_ZONE, n * sizeof(char*), false); + + for (uint32_t i = 0; i < n; ++i) { + TRI_Utf8ValueNFC arg(TRI_UNKNOWN_MEM_ZONE, arr->Get(i)); + + if (*arg == 0) { + arguments[i] = TRI_DuplicateString(""); + } + else { + arguments[i] = TRI_DuplicateString(*arg); + } + } + } + else { + n = 1; + arguments = (char**) TRI_Allocate(TRI_CORE_MEM_ZONE, n * sizeof(char*), false); + + TRI_Utf8ValueNFC arg(TRI_UNKNOWN_MEM_ZONE, a); + + if (*arg == 0) { + arguments[0] = TRI_DuplicateString(""); + } + else { + arguments[0] = TRI_DuplicateString(*arg); + } + } + } + bool usePipes = false; + if (3 <= argv.Length()) { + usePipes = TRI_ObjectToBoolean(argv[2]); + } + + TRI_external_id_t external; + TRI_CreateExternalProcess(*name, (const char**) arguments, (size_t) n, + usePipes, &external); + if (arguments != 0) { + for (uint32_t i = 0; i < n; ++i) { + TRI_FreeString(TRI_CORE_MEM_ZONE, arguments[i]); + } + + TRI_Free(TRI_CORE_MEM_ZONE, arguments); + } + if (external._pid == TRI_INVALID_PROCESS_ID) { + TRI_V8_ERROR(scope, "Process could not be started"); + } + v8::Handle result = v8::Object::New(); + result->Set(v8::String::New("pid"), v8::Number::New(external._pid)); + // Now report about possible stdin and stdout pipes: +#ifndef _WIN32 + if (external._readPipe >= 0) { + result->Set(v8::String::New("readPipe"), + v8::Number::New(external._readPipe)); + } + if (external._writePipe >= 0) { + result->Set(v8::String::New("writePipe"), + v8::Number::New(external._writePipe)); + } +#else + size_t readPipe_len, writePipe_len; + if (0 != external._readPipe) { + char* readPipe = TRI_EncodeHexString((const char *)external._readPipe, + sizeof(HANDLE), &readPipe_len); + result->Set(v8::String::New("readPipe"), + v8::String::New(readPipe, (int) readPipe_len)); + TRI_FreeString(TRI_CORE_MEM_ZONE, readPipe); + } + if (0 != external._writePipe) { + char* writePipe = TRI_EncodeHexString((const char *)external._writePipe, + sizeof(HANDLE), &writePipe_len); + result->Set(v8::String::New("writePipe"), + v8::String::New(writePipe, (int) writePipe_len)); + TRI_FreeString(TRI_CORE_MEM_ZONE, writePipe); + } +#endif + + TRI_external_id_t pid; + memset(&pid, 0, sizeof(TRI_external_id_t)); + + +#ifndef _WIN32 + pid._pid = static_cast(external._pid); +#else + pid._pid = static_cast(external._pid); +#endif + + TRI_external_status_t external_status = TRI_CheckExternalProcess(pid, true); + + const char* status = "UNKNOWN"; + + switch (external_status._status) { + case TRI_EXT_NOT_STARTED: status = "NOT-STARTED"; break; + case TRI_EXT_PIPE_FAILED: status = "FAILED"; break; + case TRI_EXT_FORK_FAILED: status = "FAILED"; break; + case TRI_EXT_RUNNING: status = "RUNNING"; break; + case TRI_EXT_NOT_FOUND: status = "NOT-FOUND"; break; + case TRI_EXT_TERMINATED: status = "TERMINATED"; break; + case TRI_EXT_ABORTED: status = "ABORTED"; break; + case TRI_EXT_STOPPED: status = "STOPPED"; break; + } + + result->Set(v8::String::New("status"), v8::String::New(status)); + + if (external_status._status == TRI_EXT_TERMINATED) { + result->Set(v8::String::New("exit"), v8::Number::New(external_status._exitStatus)); + } + else if (external_status._status == TRI_EXT_ABORTED) { + result->Set(v8::String::New("signal"), v8::Number::New(external_status._exitStatus)); + } + + // return the result + return scope.Close(result); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief kills an external process //////////////////////////////////////////////////////////////////////////////// @@ -3633,6 +3776,7 @@ void TRI_InitV8Utils (v8::Handle context, TRI_AddGlobalFunctionVocbase(context, "SYS_DOWNLOAD", JS_Download); TRI_AddGlobalFunctionVocbase(context, "SYS_EXECUTE", JS_Execute); TRI_AddGlobalFunctionVocbase(context, "SYS_EXECUTE_EXTERNAL", JS_ExecuteExternal); + TRI_AddGlobalFunctionVocbase(context, "SYS_EXECUTE_EXTERNAL_AND_WAIT", JS_ExecuteAndWaitExternal); TRI_AddGlobalFunctionVocbase(context, "SYS_GEN_RANDOM_ALPHA_NUMBERS", JS_RandomAlphaNum); TRI_AddGlobalFunctionVocbase(context, "SYS_GEN_RANDOM_NUMBERS", JS_RandomNumbers); TRI_AddGlobalFunctionVocbase(context, "SYS_GEN_RANDOM_SALT", JS_RandomSalt);