Merge latest changes from napa/vanilla

This commit is contained in:
Asi Bross 2017-06-28 14:53:19 -07:00 коммит произвёл Asi Bross
Родитель ee8e4fe673
Коммит a0c478b8c7
125 изменённых файлов: 1913 добавлений и 5618 удалений

Просмотреть файл

@ -16,7 +16,7 @@ zone.execute(
return text;
},
['hello world'])
.then((result: napa.zone.ExecuteResponse) => {
.then((result: napa.zone.Result) => {
console.log(result.value);
});
```

Просмотреть файл

@ -20,102 +20,102 @@ TEST_CASE("zone apis", "[api]") {
SECTION("broadcast valid javascript") {
napa::Zone zone("zone1");
auto response = zone.BroadcastSync("var i = 3 + 5;");
auto result = zone.BroadcastSync("var i = 3 + 5;");
REQUIRE(response == NAPA_RESPONSE_SUCCESS);
REQUIRE(result == NAPA_RESULT_SUCCESS);
}
SECTION("broadcast illegal javascript") {
napa::Zone zone("zone1");
auto response = zone.BroadcastSync("var i = 3 +");
auto result = zone.BroadcastSync("var i = 3 +");
REQUIRE(response == NAPA_RESPONSE_BROADCAST_SCRIPT_ERROR);
REQUIRE(result == NAPA_RESULT_BROADCAST_SCRIPT_ERROR);
}
SECTION("broadcast and execute javascript") {
napa::Zone zone("zone1");
auto responseCode = zone.BroadcastSync("function func(a, b) { return Number(a) + Number(b); }");
REQUIRE(responseCode == NAPA_RESPONSE_SUCCESS);
auto resultCode = zone.BroadcastSync("function func(a, b) { return Number(a) + Number(b); }");
REQUIRE(resultCode == NAPA_RESULT_SUCCESS);
napa::ExecuteRequest request;
request.function = NAPA_STRING_REF("func");
request.arguments = { NAPA_STRING_REF("2"), NAPA_STRING_REF("3") };
napa::FunctionSpec spec;
spec.function = NAPA_STRING_REF("func");
spec.arguments = { NAPA_STRING_REF("2"), NAPA_STRING_REF("3") };
auto response = zone.ExecuteSync(request);
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
REQUIRE(response.returnValue == "5");
auto result = zone.ExecuteSync(spec);
REQUIRE(result.code == NAPA_RESULT_SUCCESS);
REQUIRE(result.returnValue == "5");
}
SECTION("broadcast and execute javascript async") {
napa::Zone zone("zone1");
std::promise<napa::ExecuteResponse> promise;
std::promise<napa::Result> promise;
auto future = promise.get_future();
zone.Broadcast("function func(a, b) { return Number(a) + Number(b); }", [&promise, &zone](napa::ResponseCode) {
napa::ExecuteRequest request;
request.function = NAPA_STRING_REF("func");
request.arguments = { NAPA_STRING_REF("2"), NAPA_STRING_REF("3") };
zone.Broadcast("function func(a, b) { return Number(a) + Number(b); }", [&promise, &zone](napa::ResultCode) {
napa::FunctionSpec spec;
spec.function = NAPA_STRING_REF("func");
spec.arguments = { NAPA_STRING_REF("2"), NAPA_STRING_REF("3") };
zone.Execute(request, [&promise](napa::ExecuteResponse response) {
promise.set_value(std::move(response));
zone.Execute(spec, [&promise](napa::Result result) {
promise.set_value(std::move(result));
});
});
auto response = future.get();
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
REQUIRE(response.returnValue == "5");
auto result = future.get();
REQUIRE(result.code == NAPA_RESULT_SUCCESS);
REQUIRE(result.returnValue == "5");
}
SECTION("broadcast and execute javascript without timing out") {
napa::Zone zone("zone1");
std::promise<napa::ExecuteResponse> promise;
std::promise<napa::Result> promise;
auto future = promise.get_future();
// Warmup to avoid loading napajs on first call
zone.BroadcastSync("require('napajs');");
zone.Broadcast("function func(a, b) { return Number(a) + Number(b); }", [&promise, &zone](napa::ResponseCode) {
napa::ExecuteRequest request;
request.function = NAPA_STRING_REF("func");
request.arguments = { NAPA_STRING_REF("2"), NAPA_STRING_REF("3") };
request.options.timeout = 100;
zone.Broadcast("function func(a, b) { return Number(a) + Number(b); }", [&promise, &zone](napa::ResultCode) {
napa::FunctionSpec spec;
spec.function = NAPA_STRING_REF("func");
spec.arguments = { NAPA_STRING_REF("2"), NAPA_STRING_REF("3") };
spec.options.timeout = 100;
zone.Execute(request, [&promise](napa::ExecuteResponse response) {
promise.set_value(std::move(response));
zone.Execute(spec, [&promise](napa::Result result) {
promise.set_value(std::move(result));
});
});
auto response = future.get();
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
REQUIRE(response.returnValue == "5");
auto result = future.get();
REQUIRE(result.code == NAPA_RESULT_SUCCESS);
REQUIRE(result.returnValue == "5");
}
SECTION("broadcast and execute javascript with exceeded timeout") {
napa::Zone zone("zone1");
std::promise<napa::ExecuteResponse> promise;
std::promise<napa::Result> promise;
auto future = promise.get_future();
// Warmup to avoid loading napajs on first call
zone.BroadcastSync("require('napajs');");
zone.Broadcast("function func() { while(true) {} }", [&promise, &zone](napa::ResponseCode) {
napa::ExecuteRequest request;
request.function = NAPA_STRING_REF("func");
request.options.timeout = 200;
zone.Broadcast("function func() { while(true) {} }", [&promise, &zone](napa::ResultCode) {
napa::FunctionSpec spec;
spec.function = NAPA_STRING_REF("func");
spec.options.timeout = 200;
zone.Execute(request, [&promise](napa::ExecuteResponse response) {
promise.set_value(std::move(response));
zone.Execute(spec, [&promise](napa::Result result) {
promise.set_value(std::move(result));
});
});
auto response = future.get();
REQUIRE(response.code == NAPA_RESPONSE_TIMEOUT);
REQUIRE(response.errorMessage == "Execute exceeded timeout");
auto result = future.get();
REQUIRE(result.code == NAPA_RESULT_TIMEOUT);
REQUIRE(result.errorMessage == "Terminated due to timeout");
}
SECTION("execute 2 javascript functions, one succeeds and one times out") {
@ -125,66 +125,66 @@ TEST_CASE("zone apis", "[api]") {
zone.BroadcastSync("require('napajs');");
auto res = zone.BroadcastSync("function f1(a, b) { return Number(a) + Number(b); }");
REQUIRE(res == NAPA_RESPONSE_SUCCESS);
REQUIRE(res == NAPA_RESULT_SUCCESS);
res = zone.BroadcastSync("function f2() { while(true) {} }");
REQUIRE(res == NAPA_RESPONSE_SUCCESS);
REQUIRE(res == NAPA_RESULT_SUCCESS);
std::promise<napa::ExecuteResponse> promise1;
std::promise<napa::Result> promise1;
auto future1 = promise1.get_future();
std::promise<napa::ExecuteResponse> promise2;
std::promise<napa::Result> promise2;
auto future2 = promise2.get_future();
napa::ExecuteRequest request1;
napa::FunctionSpec request1;
request1.function = NAPA_STRING_REF("f1");
request1.arguments = { NAPA_STRING_REF("2"), NAPA_STRING_REF("3") };
request1.options.timeout = 100;
napa::ExecuteRequest request2;
napa::FunctionSpec request2;
request2.function = NAPA_STRING_REF("f2");
request2.options.timeout = 100;
zone.Execute(request1, [&promise1](napa::ExecuteResponse response) {
promise1.set_value(std::move(response));
zone.Execute(request1, [&promise1](napa::Result result) {
promise1.set_value(std::move(result));
});
zone.Execute(request2, [&promise2](napa::ExecuteResponse response) {
promise2.set_value(std::move(response));
zone.Execute(request2, [&promise2](napa::Result result) {
promise2.set_value(std::move(result));
});
auto response = future1.get();
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
REQUIRE(response.returnValue == "5");
auto result = future1.get();
REQUIRE(result.code == NAPA_RESULT_SUCCESS);
REQUIRE(result.returnValue == "5");
response = future2.get();
REQUIRE(response.code == NAPA_RESPONSE_TIMEOUT);
REQUIRE(response.errorMessage == "Execute exceeded timeout");
result = future2.get();
REQUIRE(result.code == NAPA_RESULT_TIMEOUT);
REQUIRE(result.errorMessage == "Terminated due to timeout");
}
SECTION("broadcast javascript requiring a module") {
napa::Zone zone("zone1");
auto responseCode = zone.BroadcastSync("var path = require('path'); function func() { return path.extname('test.txt'); }");
REQUIRE(responseCode == NAPA_RESPONSE_SUCCESS);
auto resultCode = zone.BroadcastSync("var path = require('path'); function func() { return path.extname('test.txt'); }");
REQUIRE(resultCode == NAPA_RESULT_SUCCESS);
napa::ExecuteRequest request;
request.function = NAPA_STRING_REF("func");
napa::FunctionSpec spec;
spec.function = NAPA_STRING_REF("func");
auto response = zone.ExecuteSync(request);
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
REQUIRE(response.returnValue == "\".txt\"");
auto result = zone.ExecuteSync(spec);
REQUIRE(result.code == NAPA_RESULT_SUCCESS);
REQUIRE(result.returnValue == "\".txt\"");
}
SECTION("execute function in a module") {
napa::Zone zone("zone1");
napa::ExecuteRequest request;
request.module = NAPA_STRING_REF("path");
request.function = NAPA_STRING_REF("extname");
request.arguments = { NAPA_STRING_REF("\"test.txt\"") };
napa::FunctionSpec spec;
spec.module = NAPA_STRING_REF("path");
spec.function = NAPA_STRING_REF("extname");
spec.arguments = { NAPA_STRING_REF("\"test.txt\"") };
auto response = zone.ExecuteSync(request);
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
REQUIRE(response.returnValue == "\".txt\"");
auto result = zone.ExecuteSync(spec);
REQUIRE(result.code == NAPA_RESULT_SUCCESS);
REQUIRE(result.returnValue == "\".txt\"");
}
}

Просмотреть файл

@ -30,7 +30,7 @@
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>4100;4251;4459;4996</DisableSpecificWarnings>
<PreprocessorDefinitions>BUILDING_NAPA_EXTENSION;NAPA_EXPORTS;BUILDING_V8_SHARED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NAPA_EXPORTS;NAPA_BINDING_EXPORTS;BUILDING_NAPA_EXTENSION;BUILDING_V8_SHARED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(NapaVanillaRoot)\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(NapaVanillaRoot)\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(NapaVanillaRoot)\test\component;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@ -49,30 +49,32 @@
<ItemGroup>
<ClCompile Include="$(NapaVanillaRoot)\src\api\api.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\memory\built-in-allocators.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\async-runner.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\binary-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\core-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\javascript-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\json-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module-cache.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module-loader-helpers.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module-resolver.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\worker-context.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\binary-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\core-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\javascript-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\json-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\module-cache.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\module-loader-helpers.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\module-resolver.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\platform\win\platform.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\platform\win\thread-local-storage.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\providers\providers.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\async-complete-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\worker.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\broadcast-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\execute-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\simple-thread-pool.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\terminable-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\timeout-service.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\settings\settings-parser.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\store\store.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\zone-impl.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\v8\v8-common.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\async-complete-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\call-context.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\call-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\eval-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\napa-zone.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\node-zone.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\simple-thread-pool.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\terminable-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\timeout-service.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\worker.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\worker-context.cpp" />
</ItemGroup>
<!-- Core modules -->
@ -82,8 +84,9 @@
<!-- Test code -->
<ItemGroup>
<ClCompile Include="main.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\worker-tests.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\node-zone-tests.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\tasks-tests.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\worker-tests.cpp" />
</ItemGroup>
<!-- Test artifacts binplace -->

Просмотреть файл

@ -14,7 +14,7 @@ private:
class NapaInitialization {
public:
NapaInitialization() {
napa::PlatformSettings settings;
napa::settings::PlatformSettings settings;
settings.loggingProvider = "console";
napa::providers::Initialize(settings);

Просмотреть файл

@ -0,0 +1,53 @@
#include "catch.hpp"
#include "zone/node-zone.h"
#include <future>
using namespace napa;
using namespace napa::zone;
TEST_CASE("node zone not available before init", "[node-zone]") {
auto zone = NodeZone::Get();
REQUIRE(zone == nullptr);
}
TEST_CASE("node zone delegate should work after init", "[node-zone]") {
auto broadcast = [](const std::string& source, BroadcastCallback callback){
callback(NAPA_RESULT_SUCCESS);
};
auto execute = [](const FunctionSpec& spec, ExecuteCallback callback) {
callback({ NAPA_RESULT_SUCCESS, "", std::string("hello world"), nullptr });
};
NodeZone::Init(broadcast, execute);
auto zone = NodeZone::Get();
REQUIRE(zone != nullptr);
{
std::promise<ResultCode> promise;
auto future = promise.get_future();
zone->Broadcast("", [&promise](ResultCode resultCode) {
promise.set_value(NAPA_RESULT_SUCCESS);
});
REQUIRE(future.get() == NAPA_RESULT_SUCCESS);
}
{
std::promise<Result> promise;
auto future = promise.get_future();
FunctionSpec spec;
zone->Execute(spec, [&promise](Result result) {
promise.set_value(std::move(result));
});
auto result = future.get();
REQUIRE(result.code == NAPA_RESULT_SUCCESS);
REQUIRE(result.returnValue == "hello world");
}
}

Просмотреть файл

@ -1,9 +1,11 @@
#include "catch.hpp"
#include "module/loader/module-loader.h"
#include "providers/providers.h"
#include "scheduler/broadcast-task.h"
#include "scheduler/execute-task.h"
#include "scheduler/task-decorators.h"
#include "zone/eval-task.h"
#include "zone/call-task.h"
#include "zone/task-decorators.h"
#include "zone/worker-context.h"
#include "settings/settings.h"
#include "v8/array-buffer-allocator.h"
#include "napa-initialization-guard.h"
@ -13,7 +15,7 @@
#include <vector>
using namespace napa;
using namespace napa::scheduler;
using namespace napa::zone;
using namespace std::chrono_literals;
// Make sure V8 it initialized exactly once.
@ -31,139 +33,142 @@ TEST_CASE("tasks", "[tasks]") {
v8::Isolate::Scope isolateScope(isolate);
v8::HandleScope handleScope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
context->SetSecurityToken(v8::Undefined(isolate));
v8::Context::Scope contextScope(context);
// Set a simple zone main function
BroadcastTask("function __zone_execute__(module, func, args) { return this[func].apply(this, args); }").Execute();
INIT_WORKER_CONTEXT();
CREATE_MODULE_LOADER();
EvalTask("require('../lib/index');").Execute();
SECTION("load valid javascript") {
ResponseCode loadResponseCode;
BroadcastTask("var i = 3 + 5;", "", [&loadResponseCode](ResponseCode code) {
ResultCode loadResponseCode;
EvalTask("var i = 3 + 5;", "", [&loadResponseCode](ResultCode code) {
loadResponseCode = code;
}).Execute();
REQUIRE(loadResponseCode == NAPA_RESPONSE_SUCCESS);
REQUIRE(loadResponseCode == NAPA_RESULT_SUCCESS);
}
SECTION("load fails when javascript is malformed") {
ResponseCode loadResponseCode;
BroadcastTask("var j = 3 +", "", [&loadResponseCode](ResponseCode code) {
ResultCode loadResponseCode;
EvalTask("var j = 3 +", "", [&loadResponseCode](ResultCode code) {
loadResponseCode = code;
}).Execute();
REQUIRE(loadResponseCode == NAPA_RESPONSE_BROADCAST_SCRIPT_ERROR);
REQUIRE(loadResponseCode == NAPA_RESULT_BROADCAST_SCRIPT_ERROR);
}
SECTION("load fails when javascript exception is thrown") {
ResponseCode loadResponseCode;
BroadcastTask("throw Error('error');", "", [&loadResponseCode](ResponseCode code) {
ResultCode loadResponseCode;
EvalTask("throw Error('error');", "", [&loadResponseCode](ResultCode code) {
loadResponseCode = code;
}).Execute();
REQUIRE(loadResponseCode == NAPA_RESPONSE_BROADCAST_SCRIPT_ERROR);
REQUIRE(loadResponseCode == NAPA_RESULT_BROADCAST_SCRIPT_ERROR);
}
SECTION("execute succeeds with a valid and existing function") {
BroadcastTask("function foo(a, b) { return Number(a) + Number(b); }").Execute();
EvalTask("function foo(a, b) { return a + b; }").Execute();
ExecuteRequest request;
request.function = NAPA_STRING_REF("foo");
request.arguments = { NAPA_STRING_REF("3"), NAPA_STRING_REF("5") };
FunctionSpec spec;
spec.function = NAPA_STRING_REF("foo");
spec.arguments = { NAPA_STRING_REF("3"), NAPA_STRING_REF("5") };
ExecuteResponse response;
ExecuteTask(request, [&](ExecuteResponse res) {
response = std::move(res);
}).Execute();
Result result;
CallTask(std::make_shared<CallContext>(spec, [&](Result res) {
result = std::move(res);
})).Execute();
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
REQUIRE(response.returnValue == "8");
REQUIRE(result.code == NAPA_RESULT_SUCCESS);
REQUIRE(result.returnValue == "8");
}
SECTION("execute fails for non-existing function") {
ExecuteRequest request;
request.function = NAPA_STRING_REF("bar");
request.arguments = { NAPA_STRING_REF("3"), NAPA_STRING_REF("5") };
FunctionSpec spec;
spec.function = NAPA_STRING_REF("bar");
spec.arguments = { NAPA_STRING_REF("3"), NAPA_STRING_REF("5") };
ExecuteResponse response;
ExecuteTask(request, [&](ExecuteResponse res) {
response = std::move(res);
}).Execute();
Result result;
CallTask(std::make_shared<CallContext>(spec, [&](Result res) {
result = std::move(res);
})).Execute();
REQUIRE(response.code == NAPA_RESPONSE_EXECUTE_FUNC_ERROR);
REQUIRE(result.code == NAPA_RESULT_EXECUTE_FUNC_ERROR);
}
SECTION("execute fails when function throws exception") {
BroadcastTask("function f1(a, b) { throw 'an error' }").Execute();
EvalTask("function f1(a, b) { throw 'an error' }").Execute();
ExecuteRequest request;
request.function = NAPA_STRING_REF("f1");
request.arguments = { NAPA_STRING_REF("3"), NAPA_STRING_REF("5") };
FunctionSpec spec;
spec.function = NAPA_STRING_REF("f1");
spec.arguments = { NAPA_STRING_REF("3"), NAPA_STRING_REF("5") };
ExecuteResponse response;
ExecuteTask(request, [&](ExecuteResponse res) {
response = std::move(res);
}).Execute();
Result result;
CallTask(std::make_shared<CallContext>(spec, [&](Result res) {
result = std::move(res);
})).Execute();
REQUIRE(response.code == NAPA_RESPONSE_EXECUTE_FUNC_ERROR);
REQUIRE(response.errorMessage == "an error");
REQUIRE(result.code == NAPA_RESULT_EXECUTE_FUNC_ERROR);
REQUIRE(result.errorMessage == "an error");
}
SECTION("execute succeeds when timeout was not exceeded") {
BroadcastTask("function f2(a, b) { return Number(a) + Number(b); }").Execute();
EvalTask("function f2(a, b) { return a + b; }").Execute();
ExecuteRequest request;
request.function = NAPA_STRING_REF("f2");
request.arguments = { NAPA_STRING_REF("3"), NAPA_STRING_REF("5") };
FunctionSpec spec;
spec.function = NAPA_STRING_REF("f2");
spec.arguments = { NAPA_STRING_REF("3"), NAPA_STRING_REF("5") };
ExecuteResponse response;
TimeoutTaskDecorator<ExecuteTask>(100ms, request, [&](ExecuteResponse res) {
response = std::move(res);
}).Execute();
Result result;
TimeoutTaskDecorator<CallTask>(100ms, std::make_shared<CallContext>(spec, [&](Result res) {
result = std::move(res);
})).Execute();
REQUIRE(response.code == NAPA_RESPONSE_SUCCESS);
REQUIRE(response.returnValue == "8");
REQUIRE(result.code == NAPA_RESULT_SUCCESS);
REQUIRE(result.returnValue == "8");
}
SECTION("execute fails when timeout exceeded") {
BroadcastTask("function f3() { while(true) {} }").Execute();
EvalTask("function f3() { while(true) {} }").Execute();
ExecuteRequest request;
request.function = NAPA_STRING_REF("f3");
FunctionSpec spec;
spec.function = NAPA_STRING_REF("f3");
ExecuteResponse response;
TimeoutTaskDecorator<ExecuteTask>(30ms, request, [&](ExecuteResponse res) {
response = std::move(res);
}).Execute();
Result result;
TimeoutTaskDecorator<CallTask>(30ms, std::make_shared<CallContext>(spec, [&](Result res) {
result = std::move(res);
})).Execute();
REQUIRE(response.code == NAPA_RESPONSE_TIMEOUT);
REQUIRE(response.errorMessage == "Execute exceeded timeout");
REQUIRE(result.code == NAPA_RESULT_TIMEOUT);
REQUIRE(result.errorMessage == "Terminated due to timeout");
}
SECTION("execute succeeds after a failed task") {
BroadcastTask("function f4() { while(true) {} }").Execute();
BroadcastTask("function f5(a, b) { return Number(a) + Number(b); }").Execute();
EvalTask("function f4() { while(true) {} }").Execute();
EvalTask("function f5(a, b) { return Number(a) + Number(b); }").Execute();
ExecuteRequest request1;
FunctionSpec request1;
request1.function = NAPA_STRING_REF("f4");
ExecuteResponse response1;
TimeoutTaskDecorator<ExecuteTask>(30ms, request1, [&](ExecuteResponse res) {
Result response1;
TimeoutTaskDecorator<CallTask>(30ms, std::make_shared<CallContext>(request1, [&](Result res) {
response1 = std::move(res);
}).Execute();
})).Execute();
REQUIRE(response1.code == NAPA_RESPONSE_TIMEOUT);
REQUIRE(response1.errorMessage == "Execute exceeded timeout");
REQUIRE(response1.code == NAPA_RESULT_TIMEOUT);
REQUIRE(response1.errorMessage == "Terminated due to timeout");
ExecuteRequest request2;
FunctionSpec request2;
request2.function = NAPA_STRING_REF("f5");
request2.arguments = { NAPA_STRING_REF("3"), NAPA_STRING_REF("5") };
ExecuteResponse response2;
TimeoutTaskDecorator<ExecuteTask>(100ms, request2, [&](ExecuteResponse res) {
Result response2;
TimeoutTaskDecorator<CallTask>(100ms, std::make_shared<CallContext>(request2, [&](Result res) {
response2 = std::move(res);
}).Execute();
})).Execute();
REQUIRE(response2.code == NAPA_RESPONSE_SUCCESS);
REQUIRE(response2.code == NAPA_RESULT_SUCCESS);
REQUIRE(response2.returnValue == "8");
}
}

Просмотреть файл

@ -1,6 +1,6 @@
#include "catch.hpp"
#include "scheduler/worker.h"
#include "zone/worker.h"
#include "napa-initialization-guard.h"
#include "v8.h"
@ -10,7 +10,9 @@
#include <mutex>
using namespace napa;
using namespace napa::scheduler;
using namespace napa::settings;
using namespace napa::zone;
class TestTask : public Task {

Просмотреть файл

@ -1,4 +1,5 @@
#include <napa-module.h>
#include <napa-async.h>
#include <atomic>
#include <functional>
@ -22,7 +23,7 @@ void Increase(const FunctionCallbackInfo<Value>& args) {
auto value = args[0]->Uint32Value();
napa::module::PostAsyncWork(Local<Function>::Cast(args[1]),
napa::zone::PostAsyncWork(Local<Function>::Cast(args[1]),
[value]() {
// This runs at the separate thread.
_now += value;
@ -51,7 +52,7 @@ void IncreaseSync(const FunctionCallbackInfo<Value>& args) {
auto value = args[0]->Uint32Value();
napa::module::DoAsyncWork(Local<Function>::Cast(args[1]),
napa::zone::DoAsyncWork(Local<Function>::Cast(args[1]),
[value](auto complete) {
// This runs at the same thread.
_now += value;

Просмотреть файл

@ -1,11 +1,11 @@
#include <catch.hpp>
#include <module/module-loader.h>
#include <napa/module/platform.h>
#include <module/loader/module-loader.h>
#include <napa/module/module-internal.h>
#include <napa/v8-helpers.h>
#include <scheduler/scheduler.h>
#include <zone/zone-impl.h>
#include <platform/platform.h>
#include <zone/scheduler.h>
#include <zone/napa-zone.h>
#include <v8/array-buffer-allocator.h>
#include <v8/v8-common.h>
@ -22,7 +22,9 @@
using namespace napa;
using namespace napa::module;
using namespace napa::scheduler;
using namespace napa::settings;
using namespace napa::zone;
class V8InitializationGuard {
public:
@ -311,7 +313,7 @@ TEST_CASE("resolve full path modules", "[module-loader]") {
class AsyncTestTask : public Task {
public:
AsyncTestTask(ZoneImpl* zone, std::string filename)
AsyncTestTask(zone::NapaZone* zone, std::string filename)
: _zone(zone), _filename(std::move(filename)), _succeeded(false) {}
void Execute() override {
@ -361,7 +363,7 @@ public:
private:
ZoneImpl* _zone;
zone::NapaZone* _zone;
std::string _filename;
bool _succeeded = false;
@ -375,7 +377,7 @@ TEST_CASE("async", "[module-loader]") {
settings.id = "zone";
settings.workers = 1;
auto zone = ZoneImpl::Create(settings);
auto zone = zone::NapaZone::Create(settings);
auto scheduler = zone->GetScheduler();
scheduler->ScheduleOnAllWorkers(std::make_shared<AsyncTestTask>(zone.get(), std::string()));

Просмотреть файл

@ -38,7 +38,7 @@
</Link>
<ClCompile>
<DisableSpecificWarnings>4100;4251;4996</DisableSpecificWarnings>
<PreprocessorDefinitions>BUILDING_NAPA_EXTENSION;NAPA_EXPORTS;BUILDING_V8_SHARED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NAPA_EXPORTS;NAPA_BINDING_EXPORTS;BUILDING_NAPA_EXTENSION;BUILDING_V8_SHARED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(NapaVanillaRoot)\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(NapaVanillaRoot)\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
@ -49,26 +49,29 @@
</ItemGroup>
<!-- To avoid exporting module-loader APIs -->
<ItemGroup>
<ClCompile Include="$(NapaVanillaRoot)\src\module\async-runner.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\binary-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\core-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\javascript-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\json-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module-cache.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module-loader-helpers.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module-resolver.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\binary-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\core-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\javascript-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\json-module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\module-cache.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\module-loader.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\module-loader-helpers.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\module-resolver.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\platform\win\platform.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\providers\providers.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\async-complete-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\broadcast-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\execute-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\simple-thread-pool.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\terminable-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\timeout-service.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\worker.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\zone-impl.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\v8\v8-common.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\async-complete-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\async-runner.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\call-context.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\call-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\eval-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\napa-zone.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\node-zone.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\simple-thread-pool.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\terminable-task.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\timeout-service.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\worker.cpp" />
</ItemGroup>
<Import Project="$(NapaVanillaRoot)\src\module\core-modules\napa\napa-core-modules.proj" />
<Import Project="$(NapaVanillaRoot)\src\module\core-modules\node\node-core-modules.proj" />

Просмотреть файл

@ -1,8 +1,9 @@
#include "catch.hpp"
#include "napa/module/platform.h"
#include "module/core-modules/node/file-system-helpers.h"
#include <platform/platform.h>
#include <module/core-modules/node/file-system-helpers.h>
using namespace napa;
using namespace napa::module;
TEST_CASE("File system helpers reads/writes a file correctly.", "[file-system-helpers]") {

Просмотреть файл

@ -1,14 +1,16 @@
#include "catch.hpp"
#include <module/module-resolver.h>
#include <napa/module/platform.h>
#include <module/loader/module-resolver.h>
#include <platform/platform.h>
#include <boost/filesystem.hpp>
#include <sstream>
using namespace napa;
using namespace napa::module;
namespace {
ModuleResolver& GetModuleResolver() {

Просмотреть файл

@ -28,7 +28,7 @@
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>4100;4996;4459</DisableSpecificWarnings>
<PreprocessorDefinitions>NAPA_EXPORTS;NAPA_LOG_DISABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NAPA_EXPORTS;NAPA_BINDING_EXPORTS;NAPA_LOG_DISABLED;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(NapaVanillaRoot)\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(NapaVanillaRoot)\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
@ -37,13 +37,13 @@
<!-- Product code that is tested and needs to compile -->
<ItemGroup>
<ClCompile Include="$(NapaVanillaRoot)\src\module\core-modules\node\file-system-helpers.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\module-resolver.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\worker-context.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\module\loader\module-resolver.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\platform\win\platform.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\platform\win\thread-local-storage.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\simple-thread-pool.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\scheduler\timeout-service.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\settings\settings-parser.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\simple-thread-pool.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\timeout-service.cpp" />
<ClCompile Include="$(NapaVanillaRoot)\src\zone\worker-context.cpp" />
</ItemGroup>
<!-- Test code -->
@ -51,8 +51,8 @@
<ClCompile Include="main.cpp" />
<ClCompile Include="module\file-system-helpers-tests.cpp" />
<ClCompile Include="module\module-resolver-tests.cpp" />
<ClCompile Include="scheduler\scheduler-tests.cpp" />
<ClCompile Include="scheduler\timeout-service-tests.cpp" />
<ClCompile Include="zone\scheduler-tests.cpp" />
<ClCompile Include="zone\timeout-service-tests.cpp" />
<ClCompile Include="settings\parser-tests.cpp" />
</ItemGroup>

Просмотреть файл

@ -7,68 +7,68 @@
using namespace napa;
TEST_CASE("Parsing nothing doesn't fail", "[settings-parser]") {
PlatformSettings settings;
settings::PlatformSettings settings;
REQUIRE(settings_parser::ParseFromString("", settings));
REQUIRE(settings_parser::ParseFromConsole(0, nullptr, settings));
REQUIRE(settings::ParseFromString("", settings));
REQUIRE(settings::ParseFromConsole(0, nullptr, settings));
}
TEST_CASE("Parsing from string", "[settings-parser]") {
PlatformSettings settings;
settings::PlatformSettings settings;
settings.workers = 1;
settings.loggingProvider = "";
REQUIRE(settings_parser::ParseFromString("--workers 5 --loggingProvider myProvider", settings));
REQUIRE(settings::ParseFromString("--workers 5 --loggingProvider myProvider", settings));
REQUIRE(settings.workers == 5);
REQUIRE(settings.loggingProvider == "myProvider");
}
TEST_CASE("Parsing from console", "[settings-parser]") {
PlatformSettings settings;
settings::PlatformSettings settings;
settings.workers = 1;
settings.loggingProvider = "";
std::vector<char*> args = { "--workers", "5", "--loggingProvider", "myProvider" };
REQUIRE(settings_parser::ParseFromConsole(static_cast<int>(args.size()), args.data(), settings));
REQUIRE(settings::ParseFromConsole(static_cast<int>(args.size()), args.data(), settings));
REQUIRE(settings.workers == 5);
REQUIRE(settings.loggingProvider == "myProvider");
}
TEST_CASE("Parsing non existing setting fails", "[settings-parser]") {
PlatformSettings settings;
settings::PlatformSettings settings;
REQUIRE(settings_parser::ParseFromString("--thisSettingDoesNotExist noValue", settings) == false);
REQUIRE(settings::ParseFromString("--thisSettingDoesNotExist noValue", settings) == false);
}
TEST_CASE("Parsing does not change defaults if setting is not provided", "[settings-parser]") {
PlatformSettings settings;
settings::PlatformSettings settings;
settings.workers = 2412;
REQUIRE(settings_parser::ParseFromString("--loggingProvider myProvider", settings));
REQUIRE(settings::ParseFromString("--loggingProvider myProvider", settings));
REQUIRE(settings.workers == 2412);
}
TEST_CASE("Parsing with extra white spaces succeeds", "[settings-parser]") {
PlatformSettings settings;
settings::PlatformSettings settings;
settings.workers = 1;
settings.loggingProvider = "";
REQUIRE(settings_parser::ParseFromString(" --workers 5 --loggingProvider \t myProvider \t\t ", settings));
REQUIRE(settings::ParseFromString(" --workers 5 --loggingProvider \t myProvider \t\t ", settings));
REQUIRE(settings.workers == 5);
REQUIRE(settings.loggingProvider == "myProvider");
}
TEST_CASE("Parsing with multiple values for one setting", "[settings-parser]") {
PlatformSettings settings;
settings::PlatformSettings settings;
settings.workers = 1;
settings.v8Flags = {};
REQUIRE(settings_parser::ParseFromString("--v8Flags one two three --workers 5", settings));
REQUIRE(settings::ParseFromString("--v8Flags one two three --workers 5", settings));
REQUIRE(settings.workers == 5);
REQUIRE(settings.v8Flags.size() == 3);
@ -78,13 +78,13 @@ TEST_CASE("Parsing with multiple values for one setting", "[settings-parser]") {
}
TEST_CASE("Parsing with empty string succeeds", "[settings-parser]") {
PlatformSettings settings;
settings::PlatformSettings settings;
REQUIRE(settings_parser::ParseFromString("", settings) == true);
REQUIRE(settings::ParseFromString("", settings) == true);
}
TEST_CASE("Parsing with different value type fails", "[settings-parser]") {
PlatformSettings settings;
settings::PlatformSettings settings;
REQUIRE(settings_parser::ParseFromString("--workers five", settings) == false);
REQUIRE(settings::ParseFromString("--workers five", settings) == false);
}

Просмотреть файл

@ -1,13 +1,13 @@
#include "catch.hpp"
#include "scheduler/scheduler.h"
#include "zone/scheduler.h"
#include <atomic>
#include <future>
using namespace napa;
using namespace napa::scheduler;
using namespace napa::zone;
using namespace napa::settings;
class TestTask : public Task {
public:

Просмотреть файл

@ -1,11 +1,11 @@
#include "catch.hpp"
#include "scheduler/timeout-service.h"
#include "zone/timeout-service.h"
#include <atomic>
#include <future>
using namespace napa::scheduler;
using namespace napa::zone;
using namespace std::chrono;
using namespace std::chrono_literals;

Просмотреть файл

@ -40,7 +40,7 @@ As you can count on NPM, most modules are pure JavaScript. These are only a few
| ------------------------------------------------------------ | ---------- | ------------- | -------------- | ------------ |
| Export JavaScript function only | | | | hello-world [[.md](../../examples/modules/hello-world/README.md) [.cpp](../../examples/modules/hello-world/node/addon.cpp) [test](../../examples/modules/hello-world/test/test.ts)] |
| Export JavaScript object (ObjectWrap) | X | | | plus-number [[.md](../../examples/modules/plus-number/README.md) [.cpp](../../examples/modules/plus-number/node/addon.cpp) [test](../../examples/modules/plus-number/test/module-test/test.ts)] |
| Share C++ object across isolates | X | X | | allocator-wrap [[.h](../../src/module/core-modules/napa-wraps/allocator-wrap.h) [.cpp](../../src/module/core-modules/napa-wraps/allocator-wrap.cpp)] |
| Share C++ object across isolates | X | X | | allocator-wrap [[.h](../../src/module/core-modules/napa/allocator-wrap.h) [.cpp](../../src/module/core-modules/napa/allocator-wrap.cpp)] |
| Export asynchronous JavaScript function | X | | X | async-number [[.md](../../examples/modules/async-number/README.md) [.cpp](../../examples/modules/async-number/node/addon.cpp) [test](../../examples/modules/async-number/test/test.ts)] |
## Special topics

Просмотреть файл

@ -2,7 +2,6 @@
## Table of Contents
- [`create(id: string, settings: ZoneSettings = DEFAULT_SETTINGS): Zone`](#create-id-string-settings-zonesettings-default_settings-zone)
- [`getOrCreate(id: string, settings: ZoneSettings = DEFAULT_SETTINGS): Zone`](#getOrCreate-id-string-settings-zonesettings-default_settings-zone)
- [`get(id: string): Zone`](#get-id-string-zone)
- [`current: Zone`](#current-zone)
- [`node: Zone`](#node-zone)
@ -15,11 +14,11 @@
- [`zone.broadcast(function: (...args: any[]) => void, args: any[]): Promise<void>`](#zone-broadcast-function-args-any-void-args-any-promise-void)
- [`zone.broadcastSync(code: string): void`](#zone-broadcastsync-code-string-void)
- [`zone.broadcastSync(function: (...args: any[]) => void, args: any[]): void`](#zone-broadcastsync-function-args-any-void-args-any-void)
- [`zone.execute(moduleName: string, functionName: string, args: any[], timeout: number): Promise<ExecuteResult>`](#zone-execute-modulename-string-functionname-string-args-any-timeout-number-promise-executeresult)
- [`zone.execute(function: (...args[]) => any, args: any[], timeout: number): Promise<ExecuteResult>`](#zone-execute-function-args-any-args-any-timeout-number-promise-executeresult)
- [`zone.executeSync(moduleName: string, functionName: string, args: any[], timeout: number): ExecuteResult`](#zone-executesync-modulename-string-functionname-string-args-any-timeout-number-executeresult)
- [`zone.executeSync(function: (...args: any[]) => any, args: any[], timeout: number): ExecuteResult`](#zone-executesync-function-args-any-any-args-any-timeout-number-executeresult)
- interface [`ExecuteResult`](#interface-executeresult)
- [`zone.execute(moduleName: string, functionName: string, args: any[], timeout: number): Promise<Result>`](#zone-execute-modulename-string-functionname-string-args-any-timeout-number-promise-result)
- [`zone.execute(function: (...args[]) => any, args: any[], timeout: number): Promise<Result>`](#zone-execute-function-args-any-args-any-timeout-number-promise-result)
- [`zone.executeSync(moduleName: string, functionName: string, args: any[], timeout: number): Result`](#zone-executesync-modulename-string-functionname-string-args-any-timeout-number-result)
- [`zone.executeSync(function: (...args: any[]) => any, args: any[], timeout: number): Result`](#zone-executesync-function-args-any-any-args-any-timeout-number-result)
- interface [`Result`](#interface-result)
- [`result.value: any`](#result-value-any)
- [`result.payload: string`](#result-payload-string)
- [`result.transportContext: transport.TransportContext`](#result-transportcontext-transport-transportcontext)
@ -40,15 +39,6 @@ let zone = napa.zone.create('zone1', {
workers: 1
});
```
### getOrCreate(id: string, settings: ZoneSettings): Zone
It gets a reference of zone by an id if a zone with the id already exists, otherwise create a new one and return its reference.
Example:
```ts
let zone = napa.zone.getOrCreate('zone1', {
workers: 4
});
```
### get(id: string): Zone
It gets a reference of zone by an id. Error will be thrown if the zone doesn't exist.
@ -154,12 +144,12 @@ catch (error) {
}
```
### zone.execute(moduleName: string, functionName: string, args: any[], timeout: number = 0): Promise\<any\>
Execute a function asynchronously on arbitrary worker via module name and function name. Arguments can be of any JavaScript type that is [transportable](transport.md#transportable-types). It returns a Promise of [`ExecuteResult`](#interface-executeresult). If error happens, either bad code, user exception, or timeout is reached, promise will be rejected.
Execute a function asynchronously on arbitrary worker via module name and function name. Arguments can be of any JavaScript type that is [transportable](transport.md#transportable-types). It returns a Promise of [`Result`](#interface-result). If error happens, either bad code, user exception, or timeout is reached, promise will be rejected.
Example: Execute function 'bar' in module 'foo', with arguments [1, 'hello', { field1: 1 }]. 300ms timeout is applied.
```ts
zone.execute('foo', 'bar', [1, "hello", {field1: 1}], 300)
.then((result: ExecuteResult) => {
.then((result: Result) => {
console.log('execute succeeded:', result.value);
})
.catch((error) => {
@ -170,14 +160,14 @@ zone.execute('foo', 'bar', [1, "hello", {field1: 1}], 300)
### zone.execute(function: (...args: any[]) => any, args: any[], timeout: number = 0): Promise\<any\>
Execute an anonymous function asynchronously on arbitrary worker. Arguments can be of any JavaScript type that is [transportable](transport.md#transportable-types). It returns a Promise of [`ExecuteResult`](#interface-executeresult). If error happens, either bad code, user exception, or timeout is reached, promise will be rejected.
Execute an anonymous function asynchronously on arbitrary worker. Arguments can be of any JavaScript type that is [transportable](transport.md#transportable-types). It returns a Promise of [`Result`](#interface-result). If error happens, either bad code, user exception, or timeout is reached, promise will be rejected.
Example:
```ts
zone.execute((a: number, b: string, c: object) => {
return a + b + JSON.stringify(c);
}, [1, "hello", {field1: 1}])
.then((result: ExecuteResult) => {
.then((result: Result) => {
console.log('execute succeeded:', result.value);
})
.catch((error) => {
@ -188,7 +178,7 @@ zone.execute((a: number, b: string, c: object) => {
### zone.executeSync(moduleName: string, functionName: string, args: any[], timeout: number = 0): any
Execute a function synchronously on arbitrary worker via module name and function name. Arguments can be of any JavaScript type that is [transportable](transport.md#transportable-types). It returns an [`ExecuteResult`](#interface-executeresult). If error happens, either bad code, user exception, or timeout is reached, error will be thrown.
Execute a function synchronously on arbitrary worker via module name and function name. Arguments can be of any JavaScript type that is [transportable](transport.md#transportable-types). It returns an [`Result`](#interface-result). If error happens, either bad code, user exception, or timeout is reached, error will be thrown.
Example: Execute function 'bar' in module 'foo', with arguments [1, 'hello', { field1: 1 }]. 300ms timeout is applied.
```ts
@ -203,7 +193,7 @@ catch (error) {
```
### zone.executeSync(function: (...args: any[]) => any, args: any[], timeout: number = 0): any
Execute an annoymouse function synchronously on arbitrary worker. Arguments can be of any JavaScript type that is [transportable](transport.md#transportable-types). It returns an [`ExecuteResult`](#interface-executeresult). If error happens, either bad code, user exception, or timeout is reached, error will be thrown.
Execute an annoymouse function synchronously on arbitrary worker. Arguments can be of any JavaScript type that is [transportable](transport.md#transportable-types). It returns an [`Result`](#interface-result). If error happens, either bad code, user exception, or timeout is reached, error will be thrown.
Example: Execute annoymouse function sychronously, with arguments [1, 'hello', { field1: 1 }]. No timeout is applied.
```ts
@ -219,7 +209,7 @@ catch (error) {
```
## Interface `ExecuteResult`
## Interface `Result`
Interface to access return value of `zone.execute` or `zone.executeSync`.
### result.value: any

Просмотреть файл

@ -1,4 +1,5 @@
#include <napa-module.h>
#include <napa-async.h>
#include <atomic>
#include <functional>
@ -22,7 +23,7 @@ void Increase(const FunctionCallbackInfo<Value>& args) {
auto value = args[0]->Uint32Value();
napa::module::PostAsyncWork(Local<Function>::Cast(args[1]),
napa::zone::PostAsyncWork(Local<Function>::Cast(args[1]),
[value]() {
// This runs at the separate thread.
_now += value;
@ -51,7 +52,7 @@ void IncreaseSync(const FunctionCallbackInfo<Value>& args) {
auto value = args[0]->Uint32Value();
napa::module::DoAsyncWork(Local<Function>::Cast(args[1]),
napa::zone::DoAsyncWork(Local<Function>::Cast(args[1]),
[value](auto complete) {
// This runs at the same thread.
_now += value;

Просмотреть файл

@ -0,0 +1,11 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(EnvironmentConfig)" />
<ItemGroup>
<ProjectFile Include="async-number\dirs.proj" />
<ProjectFile Include="hello-world\dirs.proj" />
<ProjectFile Include="plus-number\dirs.proj" />
</ItemGroup>
<Import Project="$(ExtendedTargetsPath)\Traversal.targets" />
</Project>

8
inc/napa-async.h Normal file
Просмотреть файл

@ -0,0 +1,8 @@
#pragma once
#ifdef BUILDING_NAPA_EXTENSION
#include "napa/zone/napa-async-runner.h"
#else
#include "napa/zone/node-async-runner.h"
#endif

Просмотреть файл

@ -30,7 +30,7 @@ EXTERN_C NAPA_API napa_zone_handle napa_zone_get_current();
/// <summary> Releases the zone handle. When all handles for a zone are released the zone is destoryed. </summary>
/// <param name="handle"> The zone handle. </param>
EXTERN_C NAPA_API napa_response_code napa_zone_release(napa_zone_handle handle);
EXTERN_C NAPA_API napa_result_code napa_zone_release(napa_zone_handle handle);
/// <summary>
/// Initializes the napa zone, providing specific settings.
@ -39,7 +39,7 @@ EXTERN_C NAPA_API napa_response_code napa_zone_release(napa_zone_handle handle);
/// </summary>
/// <param name="handle"> The zone handle. </param>
/// <param name="settings"> The settings string. </param>
EXTERN_C NAPA_API napa_response_code napa_zone_init(
EXTERN_C NAPA_API napa_result_code napa_zone_init(
napa_zone_handle handle,
napa_string_ref settings);
@ -60,12 +60,12 @@ EXTERN_C NAPA_API void napa_zone_broadcast(
/// <summary> Executes a pre-loaded function asynchronously in a single zone wroker. </summary>
/// <param name="handle"> The zone handle. </param>
/// <param name="request"> The execution request. </param>
/// <param name="spec"> The function spec to call. </param>
/// <param name="callback"> A callback that is triggered when execution is done. </param>
/// <param name="context"> An opaque pointer that is passed back in the callback. </param>
EXTERN_C NAPA_API void napa_zone_execute(
napa_zone_handle handle,
napa_zone_execute_request request,
napa_zone_function_spec spec,
napa_zone_execute_callback callback,
void* context);
@ -76,23 +76,23 @@ EXTERN_C NAPA_API void napa_zone_execute(
/// TODO: specify public settings here.
/// </summary>
/// <param name="settings"> The settings string. </param>
EXTERN_C NAPA_API napa_response_code napa_initialize(napa_string_ref settings);
EXTERN_C NAPA_API napa_result_code napa_initialize(napa_string_ref settings);
/// <summary>
/// Same as napa_initialize only accepts arguments as provided by console
/// </summary>
/// <param name="argc"> Number of arguments. </param>
/// <param name="argv"> The arguments. </param>
EXTERN_C NAPA_API napa_response_code napa_initialize_from_console(
EXTERN_C NAPA_API napa_result_code napa_initialize_from_console(
int argc,
char* argv[]);
/// <summary> Invokes napa shutdown steps. All non released zones will be destroyed. </summary>
EXTERN_C NAPA_API napa_response_code napa_shutdown();
EXTERN_C NAPA_API napa_result_code napa_shutdown();
/// <summary> Convert the napa response code to its string representation. </summary>
/// <param name="code"> The response code. </param>
EXTERN_C NAPA_API const char* napa_response_code_to_string(napa_response_code code);
/// <summary> Convert the napa result code to its string representation. </summary>
/// <param name="code"> The result code. </param>
EXTERN_C NAPA_API const char* napa_result_code_to_string(napa_result_code code);
/// <summary> Set customized allocator, which will be used for napa_allocate and napa_deallocate.
/// If user doesn't call napa_allocator_set, C runtime malloc/free from napa.dll will be used. </summary>

Просмотреть файл

@ -7,10 +7,8 @@
#ifdef BUILDING_NAPA_EXTENSION
#include "napa/module/module-internal.h"
#include "napa/module/module-node-compat.h"
#include "napa/module/napa-async-runner.h"
#include "napa/module/object-wrap.h"
#else
#include "napa/module/node-async-runner.h"
#include <node.h>
#include <node_object_wrap.h>
#endif

Просмотреть файл

@ -8,17 +8,17 @@
namespace napa {
/// <summary> Initializes napa with global scope settings. </summary>
inline ResponseCode Initialize(const std::string& settings = "") {
inline ResultCode Initialize(const std::string& settings = "") {
return napa_initialize(STD_STRING_TO_NAPA_STRING_REF(settings));
}
/// <summary> Initialize napa using console provided arguments. </summary>
inline ResponseCode InitializeFromConsole(int argc, char* argv[]) {
inline ResultCode InitializeFromConsole(int argc, char* argv[]) {
return napa_initialize_from_console(argc, argv);
}
/// <summary> Shut down napa. </summary>
inline ResponseCode Shutdown() {
inline ResultCode Shutdown() {
return napa_shutdown();
}
@ -33,9 +33,9 @@ namespace napa {
_handle = napa_zone_create(STD_STRING_TO_NAPA_STRING_REF(id));
auto res = napa_zone_init(_handle, STD_STRING_TO_NAPA_STRING_REF(settings));
if (res != NAPA_RESPONSE_SUCCESS) {
if (res != NAPA_RESULT_SUCCESS) {
napa_zone_release(_handle);
throw std::runtime_error(napa_response_code_to_string(res));
throw std::runtime_error(napa_result_code_to_string(res));
}
}
@ -59,7 +59,7 @@ namespace napa {
napa_zone_broadcast(
_handle,
STD_STRING_TO_NAPA_STRING_REF(source),
[](napa_response_code code, void* context) {
[](napa_result_code code, void* context) {
// Ensures the context is deleted when this scope ends.
std::unique_ptr<BroadcastCallback> callback(reinterpret_cast<BroadcastCallback*>(context));
@ -69,11 +69,11 @@ namespace napa {
/// <summary> Compiles and run the provided source code on all zone workers synchronously. </summary>
/// <param name="source"> The source code. </param>
ResponseCode BroadcastSync(const std::string& source) {
std::promise<ResponseCode> prom;
ResultCode BroadcastSync(const std::string& source) {
std::promise<ResultCode> prom;
auto fut = prom.get_future();
Broadcast(source, [&prom](ResponseCode code) {
Broadcast(source, [&prom](ResultCode code) {
prom.set_value(code);
});
@ -81,47 +81,47 @@ namespace napa {
}
/// <summary> Executes a pre-loaded JS function asynchronously. </summary>
/// <param name="request"> The execution request. </param>
/// <param name="spec"> A function spec to call. </param>
/// <param name="callback"> A callback that is triggered when execution is done. </param>
void Execute(const ExecuteRequest& request, ExecuteCallback callback) {
void Execute(const FunctionSpec& spec, ExecuteCallback callback) {
// Will be deleted on when the callback scope ends.
auto context = new ExecuteCallback(std::move(callback));
napa_zone_execute_request req;
req.module = request.module;
req.function = request.function;
req.arguments = request.arguments.data();
req.arguments_count = request.arguments.size();
req.options = request.options;
napa_zone_function_spec req;
req.module = spec.module;
req.function = spec.function;
req.arguments = spec.arguments.data();
req.arguments_count = spec.arguments.size();
req.options = spec.options;
// Release ownership of transport context
req.transport_context = reinterpret_cast<void*>(request.transportContext.release());
req.transport_context = reinterpret_cast<void*>(spec.transportContext.release());
napa_zone_execute(_handle, req, [](napa_zone_execute_response response, void* context) {
napa_zone_execute(_handle, req, [](napa_zone_result result, void* context) {
// Ensures the context is deleted when this scope ends.
std::unique_ptr<ExecuteCallback> callback(reinterpret_cast<ExecuteCallback*>(context));
ExecuteResponse res;
res.code = response.code;
res.errorMessage = NAPA_STRING_REF_TO_STD_STRING(response.error_message);
res.returnValue = NAPA_STRING_REF_TO_STD_STRING(response.return_value);
Result res;
res.code = result.code;
res.errorMessage = NAPA_STRING_REF_TO_STD_STRING(result.error_message);
res.returnValue = NAPA_STRING_REF_TO_STD_STRING(result.return_value);
// Assume ownership of transport context
res.transportContext.reset(
reinterpret_cast<napa::transport::TransportContext*>(response.transport_context));
reinterpret_cast<napa::transport::TransportContext*>(result.transport_context));
(*callback)(std::move(res));
}, context);
}
/// <summary> Executes a pre-loaded JS function synchronously. </summary>
/// <param name="request"> The execution request. </param>
ExecuteResponse ExecuteSync(const ExecuteRequest& request) {
std::promise<ExecuteResponse> prom;
/// <param name="spec"> The function spec to call. </param>
Result ExecuteSync(const FunctionSpec& spec) {
std::promise<Result> prom;
auto fut = prom.get_future();
Execute(request, [&prom](ExecuteResponse response) {
prom.set_value(std::move(response));
Execute(spec, [&prom](Result result) {
prom.set_value(std::move(result));
});
return fut.get();

Просмотреть файл

@ -1,11 +1,19 @@
#pragma once
// API exported from napa.dll
#ifdef NAPA_EXPORTS
#define NAPA_API __declspec(dllexport)
#else
#define NAPA_API __declspec(dllimport)
#endif // NAPA_EXPORTS
// API exported from napa-binding. (both napa.dll and napa-binding.node)
#ifdef NAPA_BINDING_EXPORTS
#define NAPA_BINDING_API __declspec(dllexport)
#else
#define NAPA_BINDING_API __declspec(dllimport)
#endif // NAPA_BINDING_EXPORTS
#ifdef __cplusplus
#define EXTERN_C extern "C"
#else

Просмотреть файл

@ -1,7 +1,7 @@
#pragma once
#include <napa-assert.h>
#include <napa/module/worker-context.h>
#include <napa/exports.h>
#include <napa/v8-helpers/maybe.h>
#include <napa/v8-helpers/string.h>
#include <napa/v8-helpers/flow.h>
@ -14,14 +14,7 @@ namespace binding {
/// <summary> Get 'module' object of napa binding, which is napa-binding.node in Node.JS isolate or napa-binding from core-modules in Napa isolate. </summary>
/// <returns> 'module' object for napa binding (napajs/bin/napa-binding.node or napa.dll) </returns>
inline v8::Local<v8::Object> GetModule() {
auto persistentModule =
reinterpret_cast<v8::Persistent<v8::Object>*>(
WorkerContext::Get(WorkerContextItem::NAPA_BINDING));
NAPA_ASSERT(persistentModule != nullptr, "\"napajs\" must be required before napa::module::binding::GetModule() can be called from C++.");
return v8::Local<v8::Object>::New(v8::Isolate::GetCurrent(), *persistentModule);
}
NAPA_BINDING_API v8::Local<v8::Object> GetModule();
/// <summary> Get 'module.exports' from napa binding. </summary>
/// <returns> 'module.exports' object for napa binding (napajs/bin/napa-binding.node or napa.dll) </returns>

Просмотреть файл

@ -1,7 +1,5 @@
#pragma once
#include "worker-context.h"
#include <napa/exports.h>
#include <array>
@ -91,43 +89,12 @@ namespace module {
/// <summary> It sets the persistent constructor at the current V8 isolate. </summary>
/// <param name="name"> Unique constructor name. It's recommended to use the same name as module. </param>
/// <param name="constructor"> V8 persistent function to constructor V8 object. </param>
inline void SetPersistentConstructor(const char* name,
v8::Local<v8::Function> constructor) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto constructorInfo =
static_cast<ConstructorInfo*>(WorkerContext::Get(WorkerContextItem::CONSTRUCTOR));
if (constructorInfo == nullptr) {
constructorInfo = new ConstructorInfo();
WorkerContext::Set(WorkerContextItem::CONSTRUCTOR, constructorInfo);
}
constructorInfo->constructorMap.emplace(std::piecewise_construct,
std::forward_as_tuple(name),
std::forward_as_tuple(isolate, constructor));
}
NAPA_API void SetPersistentConstructor(const char* name, v8::Local<v8::Function> constructor);
/// <summary> It gets the given persistent constructor from the current V8 isolate. </summary>
/// <param name="name"> Unique constructor name given at SetPersistentConstructor() call. </param>
/// <returns> V8 local function object. </returns>
inline v8::Local<v8::Function> GetPersistentConstructor(const char* name) {
auto isolate = v8::Isolate::GetCurrent();
v8::EscapableHandleScope scope(isolate);
auto constructorInfo =
static_cast<ConstructorInfo*>(WorkerContext::Get(WorkerContextItem::CONSTRUCTOR));
if (constructorInfo == nullptr) {
return scope.Escape(v8::Local<v8::Function>());
}
auto iter = constructorInfo->constructorMap.find(name);
if (iter != constructorInfo->constructorMap.end()) {
auto constructor = v8::Local<v8::Function>::New(isolate, iter->second);
return scope.Escape(constructor);
} else {
return scope.Escape(v8::Local<v8::Function>());
}
}
NAPA_API v8::Local<v8::Function> GetPersistentConstructor(const char* name);
} // End of namespace module.
} // End of namespace napa.

Просмотреть файл

@ -59,10 +59,16 @@ namespace module {
return _object;
}
/// <summary> Get reference of T, which is the type of contained native object. </summary>
template <typename T>
typename std::enable_if_t<!std::is_same<void, T>::value, T&> GetRef() {
return *std::static_pointer_cast<T>(_object);
}
/// <summary> It creates a new instance of WrapType of shared_ptr<T>, WrapType is a sub-class of ShareableWrap. </summary>
/// <param name="object"> shared_ptr of object. </summary>
/// <returns> V8 object of type ShareableWrap. </summary>
template <typename T, typename WrapType>
template <typename WrapType, typename T>
static v8::Local<v8::Object> NewInstance(std::shared_ptr<T> object) {
auto instance = napa::module::NewInstance<WrapType>().ToLocalChecked();
Set(instance, std::move(object));
@ -110,9 +116,9 @@ namespace module {
/// <summary> It implements TransportableObject.load(payload: object, transportContext: TransportContext): void </summary>
static void LoadCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
CHECK_ARG(isolate, args.Length() == 2, "2 arguments are required for \"load\".");
CHECK_ARG(isolate, args[0]->IsObject(), "Argument \"payload\" shall be 'Object' type.");
CHECK_ARG(isolate, args[1]->IsObject(), "Argument \"transportContext\" shall be 'TransportContextWrap' type.");
@ -134,8 +140,8 @@ namespace module {
/// <summary> It implements TransportableObject.save(payload: object, transportContext: TransportContext): void </summary>
static void SaveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
CHECK_ARG(isolate, args.Length() == 2, "2 arguments are required for \"save\".");
CHECK_ARG(isolate, args[0]->IsObject(), "Argument \"payload\" should be 'Object' type.");

Просмотреть файл

@ -1,29 +0,0 @@
///
/// Napa response codes definition file!
///
/// Guidelines:
/// 1. Use NAPA_RESPONSE_CODE_DEF to define new codes.
/// 2. Add new codes at the end of the list.
/// 3. Make sure to add a comma at the end of the previous response code defintion
///
/// |----------------------- symbol name ---------- string representation --|
/// NAPA_RESPONSE_CODE_DEF( EXAMPLE_NAME1, "Example string message1"),
/// NAPA_RESPONSE_CODE_DEF( EXAMPLE_NAME2, "Example string message2")
/// Always add news codes
///
#ifndef NAPA_RESPONSE_CODE_DEF
#error NAPA_RESPONSE_CODE_DEF must be defined before including response_code.inc
#endif
NAPA_RESPONSE_CODE_DEF( SUCCESS, "Success"),
NAPA_RESPONSE_CODE_DEF( UNDEFINED, "Undefined"),
NAPA_RESPONSE_CODE_DEF( INTERNAL_ERROR, "Napa internal error"),
NAPA_RESPONSE_CODE_DEF( TIMEOUT, "The request timed out"),
NAPA_RESPONSE_CODE_DEF( ZONE_INIT_ERROR, "Failed to initialize zone"),
NAPA_RESPONSE_CODE_DEF( BROADCAST_SCRIPT_ERROR, "Failed to broadcast JavaScript code in zone"),
NAPA_RESPONSE_CODE_DEF( EXECUTE_FUNC_ERROR, "Failed to execute the JavaScript function"),
NAPA_RESPONSE_CODE_DEF( SETTINGS_PARSER_ERROR, "Failed to parse settings"),
NAPA_RESPONSE_CODE_DEF( PROVIDERS_INIT_ERROR, "Failed to initialize providers"),
NAPA_RESPONSE_CODE_DEF( V8_INIT_ERROR, "Failed to initialize V8"),
NAPA_RESPONSE_CODE_DEF( GLOBAL_VALUE_ERROR, "Failed to set global value")

29
inc/napa/result-codes.inc Normal file
Просмотреть файл

@ -0,0 +1,29 @@
///
/// Napa result codes definition file!
///
/// Guidelines:
/// 1. Use NAPA_RESULT_CODE_DEF to define new codes.
/// 2. Add new codes at the end of the list.
/// 3. Make sure to add a comma at the end of the previous result code defintion
///
/// |----------------------- symbol name ---------- string representation --|
/// NAPA_RESULT_CODE_DEF( EXAMPLE_NAME1, "Example string message1"),
/// NAPA_RESULT_CODE_DEF( EXAMPLE_NAME2, "Example string message2")
/// Always add news codes
///
#ifndef NAPA_RESULT_CODE_DEF
#error NAPA_RESULT_CODE_DEF must be defined before including response_code.inc
#endif
NAPA_RESULT_CODE_DEF( SUCCESS, "Success"),
NAPA_RESULT_CODE_DEF( UNDEFINED, "Undefined"),
NAPA_RESULT_CODE_DEF( INTERNAL_ERROR, "Napa internal error"),
NAPA_RESULT_CODE_DEF( TIMEOUT, "The request timed out"),
NAPA_RESULT_CODE_DEF( ZONE_INIT_ERROR, "Failed to initialize zone"),
NAPA_RESULT_CODE_DEF( BROADCAST_SCRIPT_ERROR, "Failed to broadcast JavaScript code in zone"),
NAPA_RESULT_CODE_DEF( EXECUTE_FUNC_ERROR, "Failed to execute the JavaScript function"),
NAPA_RESULT_CODE_DEF( SETTINGS_PARSER_ERROR, "Failed to parse settings"),
NAPA_RESULT_CODE_DEF( PROVIDERS_INIT_ERROR, "Failed to initialize providers"),
NAPA_RESULT_CODE_DEF( V8_INIT_ERROR, "Failed to initialize V8"),
NAPA_RESULT_CODE_DEF( GLOBAL_VALUE_ERROR, "Failed to set global value")

Просмотреть файл

@ -42,8 +42,8 @@ namespace transport {
/// <summary> It implements Transportable.marshall(context: TransportContext): object </summary>
static void MarshallCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
CHECK_ARG(isolate, args.Length() == 1, "1 argument is required for calling 'marshall'.");
CHECK_ARG(isolate, args[0]->IsObject(), "The 1st argument of 'marshall' shall be object of TransportContext.");
@ -86,8 +86,8 @@ namespace transport {
/// <summary> It implements Transportable.unmarshall(payload: object, context: TransportContext): void </summary>
static void UnmarshallCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
CHECK_ARG(isolate, args.Length() == 2, "Two arguments are required for calling 'unmarshall'. ");

Просмотреть файл

@ -25,21 +25,21 @@ namespace napa {
#endif // __cplusplus
/// <summary> Represents response code in napa zone apis. </summary>
#define NAPA_RESPONSE_CODE_DEF(symbol, ...) NAPA_RESPONSE_##symbol
/// <summary> Represents result code in napa zone apis. </summary>
#define NAPA_RESULT_CODE_DEF(symbol, ...) NAPA_RESULT_##symbol
typedef enum {
#include "napa/response-codes.inc"
#include "napa/result-codes.inc"
} napa_response_code;
} napa_result_code;
#undef NAPA_RESPONSE_CODE_DEF
#undef NAPA_RESULT_CODE_DEF
#ifdef __cplusplus
namespace napa {
typedef napa_response_code ResponseCode;
typedef napa_result_code ResultCode;
}
#endif // __cplusplus
@ -66,7 +66,7 @@ namespace napa {
#endif // __cplusplus
/// <summary> Represents options for an execution request. </summary>
/// <summary> Represents options for calling a function. </summary>
typedef struct {
/// <summary> Timeout in milliseconds - Use 0 for inifinite. </summary>
@ -74,17 +74,17 @@ typedef struct {
/// <summary> Arguments transport option. Default is AUTO. </summary>
napa_transport_option transport;
} napa_zone_execute_options;
} napa_zone_call_options;
#ifdef __cplusplus
namespace napa {
typedef napa_zone_execute_options ExecuteOptions;
typedef napa_zone_call_options CallOptions;
}
#endif // __cplusplus
/// <summary> Represents an execution request for a zone. </summary>
/// <summary> Represents a function to run within a zone, with binded aruments . </summary>
typedef struct {
/// <summary> The module that exports the function to execute. </summary>
@ -100,11 +100,11 @@ typedef struct {
size_t arguments_count;
/// <summary> Options. </summary>
napa_zone_execute_options options;
napa_zone_call_options options;
/// <summary> A context used for transporting handles across zones/workers. </summary>
void* transport_context;
} napa_zone_execute_request;
} napa_zone_function_spec;
#ifdef __cplusplus
@ -114,8 +114,8 @@ typedef struct {
#include <vector>
namespace napa {
/// <summary> Represents an execution request. </summary>
struct ExecuteRequest {
/// <summary> Represents a function to call with its arguments. </summary>
struct FunctionSpec {
/// <summary> The module that exports the function to execute. </summary>
StringRef module = EMPTY_NAPA_STRING_REF;
@ -127,7 +127,7 @@ namespace napa {
std::vector<StringRef> arguments;
/// <summary> Execute options. </summary>
ExecuteOptions options = { 0, AUTO };
CallOptions options = { 0, AUTO };
/// <summary> Used for transporting shared_ptr and unique_ptr across zones/workers. </summary>
mutable std::unique_ptr<napa::transport::TransportContext> transportContext;
@ -136,11 +136,11 @@ namespace napa {
#endif // __cplusplus
/// <summary> Represents a response from executing in a zone. </summary>
/// <summary> Represents a result from executing in a zone. </summary>
typedef struct {
/// <summary> A response code. </summary>
napa_response_code code;
/// <summary> A result code. </summary>
napa_result_code code;
/// <summary> The error message in case of an error. </summary>
napa_string_ref error_message;
@ -150,16 +150,16 @@ typedef struct {
/// <summary> A context used for transporting handles across zones/workers. </summary>
void* transport_context;
} napa_zone_execute_response;
} napa_zone_result;
#ifdef __cplusplus
namespace napa {
/// <summary> Represents an execution response. </summary>
struct ExecuteResponse {
/// <summary> Represents a function call result. </summary>
struct Result {
/// <summary> A response code. </summary>
ResponseCode code;
/// <summary> A result code. </summary>
ResultCode code;
/// <summary> The error message in case of an error. </summary>
std::string errorMessage;
@ -175,16 +175,16 @@ namespace napa {
#endif // __cplusplus
/// <summary> Callback signatures. </summary>
typedef void(*napa_zone_broadcast_callback)(napa_response_code code, void* context);
typedef void(*napa_zone_execute_callback)(napa_zone_execute_response response, void* context);
typedef void(*napa_zone_broadcast_callback)(napa_result_code code, void* context);
typedef void(*napa_zone_execute_callback)(napa_zone_result result, void* context);
#ifdef __cplusplus
#include <functional>
namespace napa {
typedef std::function<void(ResponseCode)> BroadcastCallback;
typedef std::function<void(ExecuteResponse)> ExecuteCallback;
typedef std::function<void(ResultCode)> BroadcastCallback;
typedef std::function<void(Result)> ExecuteCallback;
}
#endif // __cplusplus

Просмотреть файл

@ -8,4 +8,5 @@
#include <napa/v8-helpers/json.h>
#include <napa/v8-helpers/object.h>
#include <napa/v8-helpers/ptr.h>
#include <napa/v8-helpers/string.h>
#include <napa/v8-helpers/string.h>
#include <napa/v8-helpers/time.h>

Просмотреть файл

@ -12,8 +12,8 @@ namespace v8_helpers {
/// <param name="argv"> Actual arguments. </param>
static void Log(int argc, v8::Local<v8::Value> argv[]) {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
auto console = v8::Local<v8::Object>::Cast(context->Global()->Get(
v8_helpers::MakeV8String(isolate, "console")));

Просмотреть файл

@ -19,9 +19,9 @@ namespace v8_helpers {
v8::Local<v8::Value> argv[] = nullptr) {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
v8::EscapableHandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
auto function = context->Global()->Get(v8_helpers::MakeV8String(isolate, functionName));
JS_ENSURE_WITH_RETURN(
@ -49,9 +49,9 @@ namespace v8_helpers {
v8::Local<v8::Value> argv[] = nullptr) {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
v8::EscapableHandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
auto function = object->Get(v8_helpers::MakeV8String(isolate, functionName));
JS_ENSURE_WITH_RETURN(

Просмотреть файл

@ -10,8 +10,8 @@ namespace v8_helpers {
/// <summary> JSON.stringify </summary>
/// TODO @asib: Use v8::JSON::Stringify when available
inline v8::MaybeLocal<v8::String> Stringify(v8::Isolate* isolate, const v8::Local<v8::Value>& value) {
auto context = isolate->GetCurrentContext();
v8::EscapableHandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
auto json = context->Global()
->Get(context, v8::String::NewFromUtf8(isolate, "JSON"))

Просмотреть файл

@ -0,0 +1,46 @@
#pragma once
#include <v8.h>
#include <napa/stl/string.h>
namespace napa {
namespace v8_helpers {
static const uint32_t NANOS_PER_SECOND = 1000000000;
/// <summary> Make a v8 array from high-resolution time. </summary>
inline v8::Local<v8::Array> HrtimeToV8Uint32Array(v8::Isolate* isolate, uint64_t time) {
v8::EscapableHandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
v8::Local<v8::Array> res = v8::Array::New(isolate, 2);
(void)res->CreateDataProperty(
context,
0,
v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(time / NANOS_PER_SECOND)));
(void)res->CreateDataProperty(
context,
1,
v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(time % NANOS_PER_SECOND)));
return scope.Escape(res);
}
/// <summary> Convert a 2-element v8 array to high-resolution time in nano-seconds. </summary>
inline std::pair<uint64_t, bool> V8Uint32ArrayToHrtime(v8::Isolate* isolate, v8::Local<v8::Value> value) {
v8::EscapableHandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
if (value.IsEmpty() || !value->IsArray()) {
return std::make_pair(0, false);
}
auto array = v8::Local<v8::Array>::Cast(value);
if (array->Length() != 2) {
return std::make_pair(0, false);
}
return std::make_pair(static_cast<uint64_t>(array->Get(0)->Uint32Value()) * NANOS_PER_SECOND + array->Get(1)->Uint32Value(), true);
}
}
}

Просмотреть файл

@ -7,7 +7,7 @@
#include <functional>
namespace napa {
namespace module {
namespace zone {
/// <summary> Function to run asynchronously in separate thread. </summary>
/// <remarks> Return value will be the input to 'AsyncCompleteCallback'. </remarks>

Просмотреть файл

@ -8,7 +8,7 @@
#include <vector>
namespace napa {
namespace module {
namespace zone {
/// <summary> Function to run asynchronously in separate thread. </summary>
/// <remarks> Return value will be the input to 'AsyncCompleteCallback'. </remarks>

Просмотреть файл

@ -6,4 +6,8 @@ import * as store from './store';
import * as transport from './transport';
import * as zone from './zone';
export { log, memory, metric, runtime, store, transport, zone };
export { log, memory, metric, runtime, store, transport, zone };
// Add execute proxy to global context.
import { call } from './zone/function-call';
(<any>(global))["__napa_zone_call__"] = call;

Просмотреть файл

@ -1,6 +1,5 @@
import * as napa from './zone/napa-zone';
import * as node from './zone/node-zone';
import * as zone from './zone/zone';
import * as impl from './zone/zone-impl';
import * as platform from './runtime/platform';
@ -14,31 +13,32 @@ declare var __in_napa: boolean;
/// <param name="settings"> The settings of the new zone. </param>
export function create(id: string, settings: zone.ZoneSettings = zone.DEFAULT_SETTINGS) : zone.Zone {
platform.initialize();
return new napa.NapaZone(binding.createZone(id, settings));
return new impl.ZoneImpl(binding.createZone(id, settings));
}
/// <summary> Returns the zone associated with the provided id. </summary>
export function get(id: string) : zone.Zone {
platform.initialize();
if (id === "node") {
return new node.NodeZone();
}
return new napa.NapaZone(binding.getZone(id));
return new impl.ZoneImpl(binding.getZone(id));
}
/// TODO: add function getOrCreate(id: string, settings: zone.ZoneSettings): Zone.
/// <summary> Define a getter property 'current' to retrieves the current zone. </summary>
/// <summary> Define a getter property 'current' to retrieve the current zone. </summary>
export declare let current: zone.Zone;
Object.defineProperty(exports, "current", {
get: function () : zone.Zone {
platform.initialize();
if (typeof __in_napa !== 'undefined') {
return new napa.NapaZone(binding.getCurrentZone());
}
return new impl.ZoneImpl(binding.getCurrentZone());
}
});
return new node.NodeZone();
/// <summary> Define a getter property 'node' to retrieve node zone. </summary>
export declare let node: zone.Zone;
Object.defineProperty(exports, "node", {
get: function () : zone.Zone {
platform.initialize();
return new impl.ZoneImpl(binding.getZone('node'));
}
});

145
lib/zone/function-call.ts Normal file
Просмотреть файл

@ -0,0 +1,145 @@
import * as transport from '../transport';
import { CallOptions } from './zone';
/// <summary> Rejection type </summary>
/// TODO: we need a better mapping between error code and result code.
export enum RejectionType {
TIMEOUT = 3,
APP_ERROR = 6
}
/// <summary> Interface for Call context. </summary>
export interface CallContext {
/// <summary> Resolve task with marshalled result. </summary>
resolve(result: string): void;
/// <summary> Reject task with reason. </summary>
reject(reason: any): void;
/// <summary> Reject task with a rejection type and reason. </summary>
reject(type: RejectionType, reason: any): void;
/// <summary> Returns whether task has finished (either completed or cancelled). </summary>
readonly finished: boolean;
/// <summary> Elapse in nano-seconds since task started. </summary>
readonly elapse: [number, number];
/// <summary> Module name to select function. </summary>
readonly module: string;
/// <summary> Function name to execute. </summary>
readonly function: string;
/// <summary> Marshalled arguments. </summary>
readonly args: string[];
/// <summary> Transport context. </summary>
readonly transportContext: transport.TransportContext;
/// <summary> Execute options. </summary>
readonly options: CallOptions;
}
/// <summary>
/// Proxy function for __napa_zone_call__.
/// 1) calling a global function:
/// module name: undefined or empty string
/// function name: global function name
/// 2) calling an anonymous function at client side:
/// module name: literal '__function'
/// function name: hash returned from transport.saveFunction().
/// 3) calling a function from a module:
/// module name: target module path.
/// function name: target function name from the module.
///
/// function name can have multiple levels like 'foo.bar'.
/// </summary>
export function call(context: CallContext): void {
// Cache the context since every call to context.transportContext will create a new wrap upon inner TransportContext pointer.
let transportContext = context.transportContext;
let result: any = undefined;
try {
result = callFunction(
context.module,
context.function,
context.args,
transportContext,
context.options);
}
catch(error) {
context.reject(error);
return;
}
if (result != null
&& typeof result === 'object'
&& typeof result['then'] === 'function') {
// Delay completion if return value is a promise.
result.then((value: any) => {
finishCall(context, transportContext, value);
})
.catch((error: any) => {
context.reject(error);
});
return;
}
finishCall(context, transportContext, result);
}
/// <summary> Call a function. </summary>
function callFunction(
moduleName: string,
functionName: string,
marshalledArgs: string[],
transportContext: transport.TransportContext,
options: CallOptions): any {
let module: any = null;
if (moduleName == null || moduleName.length === 0) {
module = global;
} else if (moduleName !== '__function') {
module = require(moduleName);
}
let func = null;
if (module != null) {
func = module;
if (functionName != null && functionName.length != 0) {
var path = functionName.split('.');
for (let item of path) {
func = func[item];
if (func === undefined) {
throw new Error("Cannot find function '" + functionName + "' in module '" + moduleName + "'");
}
}
}
if (typeof func !== 'function') {
throw new Error("'" + functionName + "' in module '" + moduleName + "' is not a function");
}
} else {
// Anonymous function.
func = transport.loadFunction(functionName);
}
let args = marshalledArgs.map((arg) => { return transport.unmarshall(arg, transportContext); });
return func.apply(this, args);
}
/// <summary> Finish call with result. </summary>
function finishCall(
context: CallContext,
transportContext: transport.TransportContext,
result: any) {
let payload: string = undefined;
try {
payload = transport.marshall(result, transportContext);
}
catch (error) {
context.reject(error);
return;
}
context.resolve(payload);
}

Просмотреть файл

@ -1,32 +0,0 @@
import * as zone from "./zone";
/// <summary> A virtual Zone consists only 1 worker, which is Node event loop. </summary>
export class NodeZone implements zone.Zone {
public get id(): string {
return "node";
}
public toJSON(): any {
return { id: this.id, type: "node" };
}
public broadcast(arg1: any, arg2?: any) : Promise<void> {
// TODO @asib: add implementation
return undefined;
}
public broadcastSync(arg1: any, arg2?: any) : void {
// TODO @asib: add implementation
}
public execute(arg1: any, arg2: any, arg3?: any, arg4?: any) : Promise<zone.ExecuteResult> {
// TODO @asib: add implementation
return undefined;
}
public executeSync(arg1: any, arg2: any, arg3?: any, arg4?: any) : zone.ExecuteResult {
// TODO @asib: add implementation
return undefined;
}
}

Просмотреть файл

@ -1,17 +1,15 @@
import * as zone from './zone';
import * as transport from '../transport';
declare var __in_napa: boolean;
interface ExecuteRequest {
interface FunctionSpec {
module: string;
function: string;
arguments: any[];
options: zone.ExecuteOptions;
options: zone.CallOptions;
transportContext: transport.TransportContext;
}
export class NapaExecuteResult implements zone.ExecuteResult{
class ExecuteResult implements zone.Result{
constructor(payload: string, transportContext: transport.TransportContext) {
this._payload = payload;
@ -40,7 +38,7 @@ export class NapaExecuteResult implements zone.ExecuteResult{
};
/// <summary> Zone consists of Napa isolates. </summary>
export class NapaZone implements zone.Zone {
export class ZoneImpl implements zone.Zone {
private _nativeZone: any;
constructor(nativeZone: any) {
@ -52,18 +50,18 @@ export class NapaZone implements zone.Zone {
}
public toJSON(): any {
return { id: this.id, type: "napa" };
return { id: this.id, type: this.id === 'node'? 'node': 'napa' };
}
public broadcast(arg1: any, arg2?: any) : Promise<void> {
let source: string = this.createBroadcastSource(arg1, arg2);
return new Promise<void>((resolve, reject) => {
this._nativeZone.broadcast(source, (responseCode: number) => {
if (responseCode === 0) {
this._nativeZone.broadcast(source, (resultCode: number) => {
if (resultCode === 0) {
resolve();
} else {
reject("broadcast failed with response code: " + responseCode);
reject("broadcast failed with result code: " + resultCode);
}
});
});
@ -72,36 +70,36 @@ export class NapaZone implements zone.Zone {
public broadcastSync(arg1: any, arg2?: any) : void {
let source: string = this.createBroadcastSource(arg1, arg2);
let responseCode: number = this._nativeZone.broadcastSync(source);
if (responseCode !== 0) {
throw new Error("broadcast failed with response code: " + responseCode);
let resultCode: number = this._nativeZone.broadcastSync(source);
if (resultCode !== 0) {
throw new Error("broadcast failed with result code: " + resultCode);
}
}
public execute(arg1: any, arg2: any, arg3?: any, arg4?: any) : Promise<zone.ExecuteResult> {
let request : ExecuteRequest = this.createExecuteRequest(arg1, arg2, arg3, arg4);
public execute(arg1: any, arg2: any, arg3?: any, arg4?: any) : Promise<zone.Result> {
let spec : FunctionSpec = this.createExecuteRequest(arg1, arg2, arg3, arg4);
return new Promise<zone.ExecuteResult>((resolve, reject) => {
this._nativeZone.execute(request, (response: any) => {
if (response.code === 0) {
resolve(new NapaExecuteResult(
response.returnValue,
transport.createTransportContext(response.contextHandle)));
return new Promise<zone.Result>((resolve, reject) => {
this._nativeZone.execute(spec, (result: any) => {
if (result.code === 0) {
resolve(new ExecuteResult(
result.returnValue,
transport.createTransportContext(result.contextHandle)));
} else {
reject(response.errorMessage);
reject(result.errorMessage);
}
});
});
}
public executeSync(arg1: any, arg2: any, arg3?: any, arg4?: any) : zone.ExecuteResult {
let request : ExecuteRequest = this.createExecuteRequest(arg1, arg2, arg3, arg4);
public executeSync(arg1: any, arg2: any, arg3?: any, arg4?: any) : zone.Result {
let spec : FunctionSpec = this.createExecuteRequest(arg1, arg2, arg3, arg4);
let response = this._nativeZone.executeSync(request);
if (response.code === 0) {
return new NapaExecuteResult(response.returnValue, transport.createTransportContext(response.contextHandle));
let result = this._nativeZone.executeSync(spec);
if (result.code === 0) {
return new ExecuteResult(result.returnValue, transport.createTransportContext(result.contextHandle));
} else {
throw new Error(response.errorMessage);
throw new Error(result.errorMessage);
}
}
@ -135,12 +133,12 @@ export class NapaZone implements zone.Zone {
return source;
}
private createExecuteRequest(arg1: any, arg2: any, arg3?: any, arg4?: any) : ExecuteRequest {
private createExecuteRequest(arg1: any, arg2: any, arg3?: any, arg4?: any) : FunctionSpec {
let moduleName: string = null;
let functionName: string = null;
let args: any[] = null;
let options: zone.ExecuteOptions = undefined;
let options: zone.CallOptions = undefined;
if (typeof arg1 === 'function') {
moduleName = "__function";
@ -160,7 +158,7 @@ export class NapaZone implements zone.Zone {
module: moduleName,
function: functionName,
arguments: (<Array<any>>args).map(arg => { return transport.marshall(arg, transportContext); }),
options: options != null? options: zone.DEFAULT_EXECUTE_OPTIONS,
options: options != null? options: zone.DEFAULT_CALL_OPTIONS,
transportContext: transportContext
};
}

Просмотреть файл

@ -1,34 +0,0 @@
var transport = require('../lib/transport');
function __zone_execute__(moduleName, functionName, args, transportContextHandle, options) {
var module = null;
if (moduleName == null || moduleName.length === 0) {
module = this;
} else if (moduleName !== '__function') {
module = require(moduleName);
}
var func = null;
if (module != null) {
func = module;
if (functionName != null && functionName.length != 0) {
var path = functionName.split('.');
for (item of path) {
func = func[item];
if (func === undefined) {
throw new Error("Cannot find function '" + functionName + "' in module '" + moduleName + "'");
}
}
}
if (typeof func !== 'function') {
throw new Error("'" + functionName + "' in module '" + moduleName + "' is not a function");
}
} else {
// Anonymous function.
func = transport.loadFunction(functionName);
}
var transportContext = transport.createTransportContext(transportContextHandle);
var args = args.map((arg) => { return transport.unmarshall(arg, transportContext); });
return transport.marshall(func.apply(this, args), transportContext);
}

Просмотреть файл

@ -27,8 +27,8 @@ export enum TransportOption {
MANUAL,
}
/// <summary> Represent the options of an execute call. </summary>
export interface ExecuteOptions {
/// <summary> Represent the options of calling a function. </summary>
export interface CallOptions {
/// <summary> Timeout in milliseconds. By default set to 0 if timeout is not needed. </summary>
timeout?: number,
@ -38,7 +38,7 @@ export interface ExecuteOptions {
}
/// <summary> Default execution options. </summary>
export let DEFAULT_EXECUTE_OPTIONS: ExecuteOptions = {
export let DEFAULT_CALL_OPTIONS: CallOptions = {
/// <summary> No timeout. </summary>
timeout: 0,
@ -48,7 +48,7 @@ export let DEFAULT_EXECUTE_OPTIONS: ExecuteOptions = {
}
/// <summary> Represent the result of an execute call. </summary>
export interface ExecuteResult {
export interface Result {
/// <summary> The unmarshalled result value. </summary>
readonly value : any;
@ -81,15 +81,15 @@ export interface Zone {
/// <param name="module"> The module name that contains the function to execute. </param>
/// <param name="func"> The function name to execute. </param>
/// <param name="args"> The arguments that will pass to the function. </param>
/// <param name="options"> Execute options, defaults to DEFAULT_EXECUTE_OPTIONS. </param>
execute(module: string, func: string, args: any[], options?: ExecuteOptions) : Promise<ExecuteResult>;
executeSync(module: string, func: string, args: any[], options?: ExecuteOptions) : ExecuteResult;
/// <param name="options"> Call options, defaults to DEFAULT_CALL_OPTIONS. </param>
execute(module: string, func: string, args: any[], options?: CallOptions) : Promise<Result>;
executeSync(module: string, func: string, args: any[], options?: CallOptions) : Result;
/// <summary> Executes the function on one of the zone workers. </summary>
/// <param name="func"> The JS function to execute. </param>
/// <param name="args"> The arguments that will pass to the function. </param>
/// <param name="options"> Execute options, defaults to DEFAULT_EXECUTE_OPTIONS. </param>
execute(func: Function, args: any[], timeout?: number) : Promise<ExecuteResult>;
executeSync(func: Function, args: any[], timeout?: number) : ExecuteResult;
/// <param name="options"> Call options, defaults to DEFAULT_CALL_OPTIONS. </param>
execute(func: Function, args: any[], timeout?: number) : Promise<Result>;
executeSync(func: Function, args: any[], timeout?: number) : Result;
}

Просмотреть файл

@ -1,5 +1,12 @@
# Files to compile
file(GLOB SOURCE_FILES "addon.cpp" "../src/module/core-modules/napa/*.cpp")
file(GLOB SOURCE_FILES
"addon.cpp"
"node-zone-delegates.cpp"
"${PROJECT_SOURCE_DIR}/src/zone/call-context.cpp"
"${PROJECT_SOURCE_DIR}/src/zone/call-task.cpp"
"${PROJECT_SOURCE_DIR}/src/zone/eval-task.cpp"
"${PROJECT_SOURCE_DIR}/src/zone/terminable-task.cpp"
"${PROJECT_SOURCE_DIR}/src/module/core-modules/napa/*.cpp")
# The addon name
set(TARGET_NAME "${PROJECT_NAME}-binding")
@ -13,10 +20,11 @@ set_target_properties(${TARGET_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
target_include_directories(${TARGET_NAME} PRIVATE
${CMAKE_JS_INC}
${Boost_INCLUDE_DIRS}
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/src/module/core-modules/napa)
# Compiler definitions
target_compile_definitions(${TARGET_NAME} PRIVATE BUILDING_NODE_EXTENSION)
target_compile_definitions(${TARGET_NAME} PRIVATE BUILDING_NODE_EXTENSION NAPA_BINDING_EXPORTS)
# Link libraries
target_link_libraries(${TARGET_NAME} PRIVATE

Просмотреть файл

@ -1,8 +1,11 @@
#include "napa-binding.h"
#include "node-zone-delegates.h"
#include <napa.h>
#include <napa-module.h>
#include <zone/node-zone.h>
void Initialize(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
@ -34,6 +37,8 @@ void Shutdown(const v8::FunctionCallbackInfo<v8::Value>&) {
void InitAll(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {
napa::module::binding::Init(exports, module);
napa::zone::NodeZone::Init(napa::node_zone::Broadcast, napa::node_zone::Execute);
// Only node addon can initialize/shutdown napa.
NAPA_SET_METHOD(exports, "initialize", Initialize);
NAPA_SET_METHOD(exports, "shutdown", Shutdown);

Просмотреть файл

@ -0,0 +1,52 @@
#include "node-zone-delegates.h"
#include <zone/call-task.h>
#include <zone/eval-task.h>
#include <uv.h>
struct AsyncContext {
/// <summary> libuv request. </summary>
uv_async_t work;
/// <summary> Callback that will be running in Node event loop. </summary>
std::function<void()> callback;
};
/// <summary> Run an async work item in Node. </summary>
void Run(uv_async_t* work) {
auto context = static_cast<AsyncContext*>(work->data);
context->callback();
uv_close(reinterpret_cast<uv_handle_t*>(work), [](auto work) {
auto context = static_cast<AsyncContext*>(work->data);
delete context;
});
}
/// <summary> Schedule a function in Node event loop. </summary>
void ScheduleInNode(std::function<void()> callback) {
auto context = new AsyncContext();
context->work.data = context;
context->callback = std::move(callback);
uv_async_init(uv_default_loop(), &context->work, Run);
uv_async_send(&context->work);
}
void napa::node_zone::Broadcast(const std::string& source, napa::BroadcastCallback callback) {
std::string sourceCopy = source;
ScheduleInNode([sourceCopy = std::move(sourceCopy), callback = std::move(callback)]() {
napa::zone::EvalTask task(std::move(sourceCopy), "", std::move(callback));
task.Execute();
});
}
void napa::node_zone::Execute(const napa::FunctionSpec& spec, napa::ExecuteCallback callback) {
auto requestContext = std::make_shared<napa::zone::CallContext>(spec, callback);
ScheduleInNode([requestContext = std::move(requestContext)]() {
napa::zone::CallTask task(std::move(requestContext));
task.Execute();
});
}

Просмотреть файл

@ -0,0 +1,14 @@
#pragma once
#include <napa/types.h>
namespace napa {
namespace node_zone {
/// <summary> Broadcast to Node zone. </summary>
void Broadcast(const std::string& source, napa::BroadcastCallback callback);
/// <summary> Execute in Node zone. </summary>
void Execute(const napa::FunctionSpec& spec, napa::ExecuteCallback callback);
}
}

Просмотреть файл

@ -17,7 +17,7 @@ target_include_directories(${TARGET_NAME}
${PROJECT_SOURCE_DIR}/inc)
# Compiler definitions
target_compile_definitions(${TARGET_NAME} PRIVATE NAPA_EXPORTS BUILDING_NAPA_EXTENSION)
target_compile_definitions(${TARGET_NAME} PRIVATE NAPA_EXPORTS BUILDING_NAPA_EXTENSION NAPA_BINDING_EXPORTS)
# Link libraries
target_link_libraries(${TARGET_NAME} PRIVATE ${Boost_LIBRARIES})

Просмотреть файл

@ -3,7 +3,8 @@
#include "providers/providers.h"
#include "settings/settings-parser.h"
#include "v8/v8-common.h"
#include "zone/zone-impl.h"
#include "zone/napa-zone.h"
#include "zone/node-zone.h"
#include <napa-log.h>
@ -20,12 +21,12 @@
using namespace napa;
static std::atomic<bool> _initialized(false);
static PlatformSettings _platformSettings;
static settings::PlatformSettings _platformSettings;
/// <summary> a simple wrapper around Zone for managing lifetime using shared_ptr. </summary>
struct napa_zone {
std::string id;
std::shared_ptr<internal::Zone> zone;
std::shared_ptr<zone::Zone> zone;
};
napa_zone_handle napa_zone_create(napa_string_ref id) {
@ -39,7 +40,13 @@ napa_zone_handle napa_zone_get(napa_string_ref id) {
NAPA_ASSERT(_initialized, "Napa wasn't initialized");
auto zoneId = NAPA_STRING_REF_TO_STD_STRING(id);
auto zone = ZoneImpl::Get(zoneId);
std::shared_ptr<zone::Zone> zone;
if (zoneId == "node") {
zone = zone::NodeZone::Get();
} else {
zone = zone::NapaZone::Get(zoneId);
}
if (!zone) {
return nullptr;
}
@ -50,7 +57,7 @@ napa_zone_handle napa_zone_get(napa_string_ref id) {
napa_zone_handle napa_zone_get_current() {
NAPA_ASSERT(_initialized, "Napa wasn't initialized");
auto zone = reinterpret_cast<ZoneImpl*>(napa::module::WorkerContext::Get(napa::module::WorkerContextItem::ZONE));
auto zone = reinterpret_cast<zone::Zone*>(zone::WorkerContext::Get(zone::WorkerContextItem::ZONE));
if (zone == nullptr) {
LOG_WARNING("Api", "Trying to get current zone from a thread that is not associated with a zone");
return nullptr;
@ -59,39 +66,39 @@ napa_zone_handle napa_zone_get_current() {
return napa_zone_get(STD_STRING_TO_NAPA_STRING_REF(zone->GetId()));
}
napa_response_code napa_zone_init(napa_zone_handle handle, napa_string_ref settings) {
napa_result_code napa_zone_init(napa_zone_handle handle, napa_string_ref settings) {
NAPA_ASSERT(_initialized, "Napa wasn't initialized");
NAPA_ASSERT(handle, "Zone handle is null");
// Zone settings are based on platform settings.
ZoneSettings zoneSettings = _platformSettings;
if (!napa::settings_parser::ParseFromString(NAPA_STRING_REF_TO_STD_STRING(settings), zoneSettings)) {
settings::ZoneSettings zoneSettings = _platformSettings;
if (!napa::settings::ParseFromString(NAPA_STRING_REF_TO_STD_STRING(settings), zoneSettings)) {
LOG_ERROR("Api", "Failed to parse zone settings: %s", settings.data);
return NAPA_RESPONSE_SETTINGS_PARSER_ERROR;
return NAPA_RESULT_SETTINGS_PARSER_ERROR;
}
zoneSettings.id = handle->id;
// Create the actual zone.
handle->zone = ZoneImpl::Create(zoneSettings);
handle->zone = zone::NapaZone::Create(zoneSettings);
if (handle->zone == nullptr) {
LOG_ERROR("Api", "Failed to initialize zone: %s", handle->id.c_str());
return NAPA_RESPONSE_ZONE_INIT_ERROR;
return NAPA_RESULT_ZONE_INIT_ERROR;
}
LOG_INFO("Api", "Napa zone '%s' initialized successfully", handle->id.c_str());
return NAPA_RESPONSE_SUCCESS;
return NAPA_RESULT_SUCCESS;
}
napa_response_code napa_zone_release(napa_zone_handle handle) {
napa_result_code napa_zone_release(napa_zone_handle handle) {
NAPA_ASSERT(_initialized, "Napa wasn't initialized");
NAPA_ASSERT(handle, "Zone handle is null");
handle->zone = nullptr;
delete handle;
return NAPA_RESPONSE_SUCCESS;
return NAPA_RESULT_SUCCESS;
}
napa_string_ref napa_zone_get_id(napa_zone_handle handle) {
@ -109,54 +116,54 @@ void napa_zone_broadcast(napa_zone_handle handle,
NAPA_ASSERT(handle, "Zone handle is null");
NAPA_ASSERT(handle->zone, "Zone handle wasn't initialized");
handle->zone->Broadcast(NAPA_STRING_REF_TO_STD_STRING(source), [callback, context](napa_response_code code) {
handle->zone->Broadcast(NAPA_STRING_REF_TO_STD_STRING(source), [callback, context](napa_result_code code) {
callback(code, context);
});
}
void napa_zone_execute(napa_zone_handle handle,
napa_zone_execute_request request,
napa_zone_function_spec spec,
napa_zone_execute_callback callback,
void* context) {
NAPA_ASSERT(_initialized, "Napa wasn't initialized");
NAPA_ASSERT(handle, "Zone handle is null");
NAPA_ASSERT(handle->zone, "Zone handle wasn't initialized");
ExecuteRequest req;
req.module = request.module;
req.function = request.function;
FunctionSpec req;
req.module = spec.module;
req.function = spec.function;
req.arguments.reserve(request.arguments_count);
for (size_t i = 0; i < request.arguments_count; i++) {
req.arguments.emplace_back(request.arguments[i]);
req.arguments.reserve(spec.arguments_count);
for (size_t i = 0; i < spec.arguments_count; i++) {
req.arguments.emplace_back(spec.arguments[i]);
}
req.options = request.options;
req.options = spec.options;
// Assume ownership of transport context
req.transportContext.reset(reinterpret_cast<napa::transport::TransportContext*>(request.transport_context));
req.transportContext.reset(reinterpret_cast<napa::transport::TransportContext*>(spec.transport_context));
handle->zone->Execute(req, [callback, context](ExecuteResponse response) {
napa_zone_execute_response res;
res.code = response.code;
res.error_message = STD_STRING_TO_NAPA_STRING_REF(response.errorMessage);
res.return_value = STD_STRING_TO_NAPA_STRING_REF(response.returnValue);
handle->zone->Execute(req, [callback, context](Result result) {
napa_zone_result res;
res.code = result.code;
res.error_message = STD_STRING_TO_NAPA_STRING_REF(result.errorMessage);
res.return_value = STD_STRING_TO_NAPA_STRING_REF(result.returnValue);
// Release ownership of transport context
res.transport_context = reinterpret_cast<void*>(response.transportContext.release());
res.transport_context = reinterpret_cast<void*>(result.transportContext.release());
callback(res, context);
});
}
static napa_response_code napa_initialize_common() {
static napa_result_code napa_initialize_common() {
if (!napa::providers::Initialize(_platformSettings)) {
return NAPA_RESPONSE_PROVIDERS_INIT_ERROR;
return NAPA_RESULT_PROVIDERS_INIT_ERROR;
}
if (_platformSettings.initV8) {
if (!napa::v8_common::Initialize()) {
return NAPA_RESPONSE_V8_INIT_ERROR;
return NAPA_RESULT_V8_INIT_ERROR;
}
}
@ -164,30 +171,30 @@ static napa_response_code napa_initialize_common() {
LOG_INFO("Api", "Napa initialized successfully");
return NAPA_RESPONSE_SUCCESS;
return NAPA_RESULT_SUCCESS;
}
napa_response_code napa_initialize(napa_string_ref settings) {
napa_result_code napa_initialize(napa_string_ref settings) {
NAPA_ASSERT(!_initialized, "Napa was already initialized");
if (!napa::settings_parser::ParseFromString(NAPA_STRING_REF_TO_STD_STRING(settings), _platformSettings)) {
return NAPA_RESPONSE_SETTINGS_PARSER_ERROR;
if (!napa::settings::ParseFromString(NAPA_STRING_REF_TO_STD_STRING(settings), _platformSettings)) {
return NAPA_RESULT_SETTINGS_PARSER_ERROR;
}
return napa_initialize_common();
}
napa_response_code napa_initialize_from_console(int argc, char* argv[]) {
napa_result_code napa_initialize_from_console(int argc, char* argv[]) {
NAPA_ASSERT(!_initialized, "Napa was already initialized");
if (!napa::settings_parser::ParseFromConsole(argc, argv, _platformSettings)) {
return NAPA_RESPONSE_SETTINGS_PARSER_ERROR;
if (!napa::settings::ParseFromConsole(argc, argv, _platformSettings)) {
return NAPA_RESULT_SETTINGS_PARSER_ERROR;
}
return napa_initialize_common();
}
napa_response_code napa_shutdown() {
napa_result_code napa_shutdown() {
NAPA_ASSERT(_initialized, "Napa wasn't initialized");
napa::providers::Shutdown();
@ -198,23 +205,23 @@ napa_response_code napa_shutdown() {
LOG_INFO("Api", "Napa shutdown successfully");
return NAPA_RESPONSE_SUCCESS;
return NAPA_RESULT_SUCCESS;
}
#define NAPA_RESPONSE_CODE_DEF(symbol, string_rep) string_rep
#define NAPA_RESULT_CODE_DEF(symbol, string_rep) string_rep
static const char* NAPA_REPONSE_CODE_STRINGS[] = {
#include "napa/response-codes.inc"
#include "napa/result-codes.inc"
};
#undef NAPA_RESPONSE_CODE_DEF
#undef NAPA_RESULT_CODE_DEF
template<class T, size_t N>
constexpr size_t size(T(&)[N]) { return N; }
const char* napa_response_code_to_string(napa_response_code code) {
NAPA_ASSERT(code < size(NAPA_REPONSE_CODE_STRINGS), "response code out of range");
const char* napa_result_code_to_string(napa_result_code code) {
NAPA_ASSERT(code < size(NAPA_REPONSE_CODE_STRINGS), "result code out of range");
return NAPA_REPONSE_CODE_STRINGS[code];
}

Просмотреть файл

@ -0,0 +1,144 @@
#include "call-context-wrap.h"
#include "transport-context-wrap-impl.h"
#include <napa/module/common.h>
#include <napa-transport.h>
using namespace napa;
using namespace napa::module;
NAPA_DEFINE_PERSISTENT_CONSTRUCTOR(CallContextWrap)
void CallContextWrap::Init() {
auto isolate = v8::Isolate::GetCurrent();
auto constructorTemplate = v8::FunctionTemplate::New(isolate, DefaultConstructorCallback<CallContextWrap>);
constructorTemplate->SetClassName(v8_helpers::MakeV8String(isolate, exportName));
constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
InitConstructorTemplate<CallContextWrap>(constructorTemplate);
NAPA_SET_PROTOTYPE_METHOD(constructorTemplate, "resolve", ResolveCallback);
NAPA_SET_PROTOTYPE_METHOD(constructorTemplate, "reject", RejectCallback);
NAPA_SET_ACCESSOR(constructorTemplate, "finished", IsFinishedCallback, nullptr);
NAPA_SET_ACCESSOR(constructorTemplate, "elapse", GetElapseCallback, nullptr);
NAPA_SET_ACCESSOR(constructorTemplate, "module", GetModuleCallback, nullptr);
NAPA_SET_ACCESSOR(constructorTemplate, "function", GetFunctionCallback, nullptr);
NAPA_SET_ACCESSOR(constructorTemplate, "args", GetArgumentsCallback, nullptr);
NAPA_SET_ACCESSOR(constructorTemplate, "transportContext", GetTransportContextCallback, nullptr);
NAPA_SET_ACCESSOR(constructorTemplate, "options", GetOptionsCallback, nullptr);
auto constructor = constructorTemplate->GetFunction();
InitConstructor("<CallContextWrap>", constructor);
NAPA_SET_PERSISTENT_CONSTRUCTOR(exportName, constructor);
}
v8::Local<v8::Object> CallContextWrap::NewInstance(std::shared_ptr<zone::CallContext> call) {
return ShareableWrap::NewInstance<CallContextWrap>(call);
}
zone::CallContext& CallContextWrap::GetRef() {
return ShareableWrap::GetRef<zone::CallContext>();
}
void CallContextWrap::ResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
CHECK_ARG(isolate, args.Length() == 1, "1 argument of 'result' is required for \"resolve\".");
v8::String::Utf8Value result(args[0]);
auto thisObject = NAPA_OBJECTWRAP::Unwrap<CallContextWrap>(args.Holder());
auto success = thisObject->GetRef().Resolve(std::string(*result, result.length()));
JS_ENSURE(isolate, success, "Resolve call failed: Already finished.");
}
void CallContextWrap::RejectCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
CHECK_ARG(isolate, args.Length() == 1 || args.Length() == 2, "at least 1 argument of 'reason' is required for \"reject\".");
napa::ResultCode code = NAPA_RESULT_EXECUTE_FUNC_ERROR;
v8::Local<v8::Value> reason;
if (args.Length() == 1) {
reason = args[0];
} else {
CHECK_ARG(isolate, args[0]->IsUint32(), "arg 'resultCode' should be a number type.");
code = static_cast<napa::ResultCode>(args[0]->Uint32Value());
reason = args[1];
}
v8::String::Utf8Value reasonStr(reason);
auto thisObject = NAPA_OBJECTWRAP::Unwrap<CallContextWrap>(args.Holder());
auto success = thisObject->GetRef().Reject(code, std::string(*reasonStr, reasonStr.length()));
JS_ENSURE(isolate, success, "Reject call failed: Already finished.");
}
void CallContextWrap::IsFinishedCallback(v8::Local<v8::String> /*propertyName*/, const v8::PropertyCallbackInfo<v8::Value>& args){
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto thisObject = NAPA_OBJECTWRAP::Unwrap<CallContextWrap>(args.Holder());
args.GetReturnValue().Set(thisObject->GetRef().IsFinished());
}
void CallContextWrap::GetElapseCallback(v8::Local<v8::String> /*propertyName*/, const v8::PropertyCallbackInfo<v8::Value>& args){
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto thisObject = NAPA_OBJECTWRAP::Unwrap<CallContextWrap>(args.Holder());
args.GetReturnValue().Set(v8_helpers::HrtimeToV8Uint32Array(isolate, thisObject->GetRef().GetElapse().count()));
}
void CallContextWrap::GetModuleCallback(v8::Local<v8::String> /*propertyName*/, const v8::PropertyCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto thisObject = NAPA_OBJECTWRAP::Unwrap<CallContextWrap>(args.Holder());
args.GetReturnValue().Set(v8_helpers::MakeV8String(isolate, thisObject->GetRef().GetModule()));
}
void CallContextWrap::GetFunctionCallback(v8::Local<v8::String> /*propertyName*/, const v8::PropertyCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto thisObject = NAPA_OBJECTWRAP::Unwrap<CallContextWrap>(args.Holder());
args.GetReturnValue().Set(v8_helpers::MakeV8String(isolate, thisObject->GetRef().GetFunction()));
}
void CallContextWrap::GetArgumentsCallback(v8::Local<v8::String> /*propertyName*/, const v8::PropertyCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
auto thisObject = NAPA_OBJECTWRAP::Unwrap<CallContextWrap>(args.Holder());
auto& cppArgs = thisObject->GetRef().GetArguments();
auto jsArgs = v8::Array::New(isolate, static_cast<int>(cppArgs.size()));
for (size_t i = 0; i < cppArgs.size(); ++i) {
(void)jsArgs->CreateDataProperty(context, static_cast<uint32_t>(i), v8_helpers::MakeExternalV8String(isolate, cppArgs[i]));
}
args.GetReturnValue().Set(jsArgs);
}
void CallContextWrap::GetTransportContextCallback(v8::Local<v8::String> /*propertyName*/, const v8::PropertyCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
auto thisObject = NAPA_OBJECTWRAP::Unwrap<CallContextWrap>(args.Holder());
auto& transportContext = thisObject->GetRef().GetTransportContext();
auto wrap = TransportContextWrapImpl::NewInstance(&transportContext);
args.GetReturnValue().Set(wrap);
}
void CallContextWrap::GetOptionsCallback(v8::Local<v8::String> /*propertyName*/, const v8::PropertyCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
// Prepare execute options.
// NOTE: export necessary fields from CallContext.GetOptions to jsOptions object here. Now it's empty.
auto jsOptions = v8::ObjectTemplate::New(isolate)->NewInstance();
args.GetReturnValue().Set(jsOptions);
}

Просмотреть файл

@ -0,0 +1,63 @@
#pragma once
#include <napa-module.h>
#include <napa/module/common.h>
#include <napa/module/shareable-wrap.h>
#include <zone/call-context.h>
namespace napa {
namespace module {
/// <summary>
/// Class that wraps zone::CallContext, which enables JavaScript world to
/// resolve or reject a function call.
/// </summary>
class CallContextWrap: public ShareableWrap {
public:
/// <summary> It creates a persistent constructor for CallContextWrap instance. </summary>
static void Init();
/// <summary> Create a new instance of wrap associating with specific call context. </summary>
static v8::Local<v8::Object> NewInstance(std::shared_ptr<zone::CallContext> call);
/// <summary> Get call context. </summary>
zone::CallContext& GetRef();
/// <summary> Exported class name. </summary>
static constexpr const char* exportName = "CallContextWrap";
/// <summary> Declare constructor in public, so we can export class constructor in JavaScript world. </summary>
NAPA_DECLARE_PERSISTENT_CONSTRUCTOR
protected:
/// <summary> It implements CallContext.resolve(result: any): void </summary>
static void ResolveCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
/// <summary> It implements CallContext.reject(reason: string): void </summary>
static void RejectCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
/// <summary> It implements CallContext.finished: boolean </summary>
static void IsFinishedCallback(v8::Local<v8::String> propertyName, const v8::PropertyCallbackInfo<v8::Value>& args);
/// <summary> It implements CallContext.module: string </summary>
static void GetModuleCallback(v8::Local<v8::String> propertyName, const v8::PropertyCallbackInfo<v8::Value>& args);
/// <summary> It implements CallContext.function: string </summary>
static void GetFunctionCallback(v8::Local<v8::String> propertyName, const v8::PropertyCallbackInfo<v8::Value>& args);
/// <summary> It implements CallContext.args: string[] </summary>
static void GetArgumentsCallback(v8::Local<v8::String> propertyName, const v8::PropertyCallbackInfo<v8::Value>& args);
/// <summary> It implements CallContext.transportContext: TransportContext </summary>
static void GetTransportContextCallback(v8::Local<v8::String> propertyName, const v8::PropertyCallbackInfo<v8::Value>& args);
/// <summary> It implements CallContext.options: CallOptions </summary>
static void GetOptionsCallback(v8::Local<v8::String> propertyName, const v8::PropertyCallbackInfo<v8::Value>& args);
/// <summary> It implements CallContext.elapse: [number, number] (precision in nano-second) </summary>
static void GetElapseCallback(v8::Local<v8::String> propertyName, const v8::PropertyCallbackInfo<v8::Value>& args);
};
}
}

Просмотреть файл

@ -3,15 +3,18 @@
#include "metric-wrap.h"
#include "allocator-debugger-wrap.h"
#include "allocator-wrap.h"
#include "call-context-wrap.h"
#include "shared-ptr-wrap.h"
#include "store-wrap.h"
#include "transport-context-wrap-impl.h"
#include "zone-wrap.h"
#include <zone/worker-context.h>
#include <napa.h>
#include <napa-memory.h>
#include <napa/module/binding.h>
#include <napa/module/binding/wraps.h>
#include <napa/module/worker-context.h>
#include <napa/providers/logging.h>
#include <napa/providers/metric.h>
@ -23,7 +26,16 @@ static void RegisterBinding(v8::Local<v8::Object> module) {
v8::HandleScope scope(isolate);
auto persistentModule = new v8::Persistent<v8::Object>(isolate, module);
WorkerContext::Set(WorkerContextItem::NAPA_BINDING, persistentModule);
zone::WorkerContext::Set(zone::WorkerContextItem::NAPA_BINDING, persistentModule);
}
v8::Local<v8::Object> napa::module::binding::GetModule() {
auto persistentModule =
reinterpret_cast<v8::Persistent<v8::Object>*>(
zone::WorkerContext::Get(zone::WorkerContextItem::NAPA_BINDING));
NAPA_ASSERT(persistentModule != nullptr, "\"napajs\" must be required before napa::module::binding::GetModule() can be called from C++.");
return v8::Local<v8::Object>::New(v8::Isolate::GetCurrent(), *persistentModule);
}
static void CreateZone(const v8::FunctionCallbackInfo<v8::Value>& args) {
@ -194,6 +206,7 @@ void binding::Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module)
AllocatorDebuggerWrap::Init();
AllocatorWrap::Init();
MetricWrap::Init();
CallContextWrap::Init();
SharedPtrWrap::Init();
StoreWrap::Init();
TransportContextWrapImpl::Init();
@ -202,6 +215,7 @@ void binding::Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module)
NAPA_EXPORT_OBJECTWRAP(exports, "AllocatorDebuggerWrap", AllocatorDebuggerWrap);
NAPA_EXPORT_OBJECTWRAP(exports, "AllocatorWrap", AllocatorWrap);
NAPA_EXPORT_OBJECTWRAP(exports, "MetricWrap", MetricWrap);
NAPA_EXPORT_OBJECTWRAP(exports, "CallContextWrap", CallContextWrap);
NAPA_EXPORT_OBJECTWRAP(exports, "SharedPtrWrap", SharedPtrWrap);
NAPA_EXPORT_OBJECTWRAP(exports, "TransportContextWrap", TransportContextWrapImpl);

Просмотреть файл

@ -2,6 +2,7 @@
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)\allocator-debugger-wrap.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\allocator-wrap.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\call-context-wrap.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\metric-wrap.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\napa-binding.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)\shared-ptr-wrap.cpp" />

Просмотреть файл

@ -12,6 +12,14 @@ TransportContextWrapImpl::TransportContextWrapImpl(TransportContext* context) {
_context = context;
}
v8::Local<v8::Object> TransportContextWrapImpl::NewInstance(napa::transport::TransportContext* context) {
auto object = napa::module::NewInstance<TransportContextWrapImpl>().ToLocalChecked();
auto wrap = NAPA_OBJECTWRAP::Unwrap<TransportContextWrapImpl>(object);
wrap->_context = context;
return object;
}
TransportContext* TransportContextWrapImpl::Get() {
return _context;
}
@ -19,14 +27,14 @@ TransportContext* TransportContextWrapImpl::Get() {
void TransportContextWrapImpl::Init() {
auto isolate = v8::Isolate::GetCurrent();
auto constructorTemplate = v8::FunctionTemplate::New(isolate, TransportContextWrapImpl::ConstructorCallback);
constructorTemplate->SetClassName(MakeV8String(isolate, _exportName));
constructorTemplate->SetClassName(MakeV8String(isolate, exportName));
constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
NAPA_SET_PROTOTYPE_METHOD(constructorTemplate, "saveShared", SaveSharedCallback);
NAPA_SET_PROTOTYPE_METHOD(constructorTemplate, "loadShared", LoadSharedCallback);
NAPA_SET_ACCESSOR(constructorTemplate, "sharedCount", GetSharedCountCallback, nullptr);
NAPA_SET_PERSISTENT_CONSTRUCTOR(_exportName, constructorTemplate->GetFunction());
NAPA_SET_PERSISTENT_CONSTRUCTOR(exportName, constructorTemplate->GetFunction());
}
void TransportContextWrapImpl::GetSharedCountCallback(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>& args){

Просмотреть файл

@ -13,12 +13,18 @@ namespace module {
/// <summary> Init this wrap. </summary>
static void Init();
/// <summary> Create a non-owning transport context wrap. </summary>
static v8::Local<v8::Object> NewInstance(napa::transport::TransportContext* context);
/// <summary> Get transport context. </summary>
napa::transport::TransportContext* Get() override;
/// <summary> Declare constructor in public, so we can export class constructor to JavaScript world. </summary>
NAPA_DECLARE_PERSISTENT_CONSTRUCTOR
/// <summary> Exported class name. </summary>
static constexpr const char* exportName = "TransportContextWrap";
private:
/// <summary> Constructor. </summary>
TransportContextWrapImpl(napa::transport::TransportContext* context);
@ -39,9 +45,6 @@ namespace module {
/// <summary> It implements TransportContext.loadShared(handle: Handle): napajs.memory.ShareableWrap) </summary>
static void LoadSharedCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
/// <summary> Exported class name. </summary>
static constexpr const char* _exportName = "TransportContextWrap";
/// <summary> Non-owning transport context. </summary>
napa::transport::TransportContext* _context;
};

Просмотреть файл

@ -4,6 +4,7 @@
#include <napa.h>
#include <napa-assert.h>
#include <napa-async.h>
#include <napa/v8-helpers.h>
#include <sstream>
@ -15,7 +16,7 @@ using namespace napa::v8_helpers;
NAPA_DEFINE_PERSISTENT_CONSTRUCTOR(ZoneWrap);
// Forward declaration.
static v8::Local<v8::Object> CreateResponseObject(const napa::ExecuteResponse& response);
static v8::Local<v8::Object> CreateResponseObject(const napa::Result& result);
template <typename Func>
static void CreateRequestAndExecute(v8::Local<v8::Object> obj, Func&& func);
@ -66,23 +67,22 @@ void ZoneWrap::Broadcast(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::String::Utf8Value source(args[0]->ToString());
napa::module::DoAsyncWork(v8::Local<v8::Function>::Cast(args[1]),
napa::zone::DoAsyncWork(v8::Local<v8::Function>::Cast(args[1]),
[&args, &source](std::function<void(void*)> complete) {
auto wrap = ObjectWrap::Unwrap<ZoneWrap>(args.Holder());
wrap->_zoneProxy->Broadcast(*source, [complete = std::move(complete)](ResponseCode responseCode) {
complete(reinterpret_cast<void*>(static_cast<uintptr_t>(responseCode)));
wrap->_zoneProxy->Broadcast(*source, [complete = std::move(complete)](ResultCode resultCode) {
complete(reinterpret_cast<void*>(static_cast<uintptr_t>(resultCode)));
});
},
[](auto jsCallback, void* result) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
v8::HandleScope scope(isolate);
std::vector<v8::Local<v8::Value>> argv;
auto responseCode = static_cast<ResponseCode>(reinterpret_cast<uintptr_t>(result));
argv.emplace_back(v8::Uint32::NewFromUnsigned(isolate, responseCode));
auto resultCode = static_cast<ResultCode>(reinterpret_cast<uintptr_t>(result));
argv.emplace_back(v8::Uint32::NewFromUnsigned(isolate, resultCode));
(void)jsCallback->Call(context, context->Global(), static_cast<int>(argv.size()), argv.data());
}
@ -97,41 +97,41 @@ void ZoneWrap::BroadcastSync(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::String::Utf8Value source(args[0]->ToString());
auto wrap = ObjectWrap::Unwrap<ZoneWrap>(args.Holder());
auto responseCode = wrap->_zoneProxy->BroadcastSync(*source);
auto resultCode = wrap->_zoneProxy->BroadcastSync(*source);
args.GetReturnValue().Set(v8::Uint32::NewFromUnsigned(isolate, responseCode));
args.GetReturnValue().Set(v8::Uint32::NewFromUnsigned(isolate, resultCode));
}
void ZoneWrap::Execute(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
CHECK_ARG(isolate, args[0]->IsObject(), "first argument to zone.execute must be the execution request object");
CHECK_ARG(isolate, args[0]->IsObject(), "first argument to zone.execute must be the function spec object");
CHECK_ARG(isolate, args[1]->IsFunction(), "second argument to zone.execute must be the callback");
napa::module::DoAsyncWork(v8::Local<v8::Function>::Cast(args[1]),
napa::zone::DoAsyncWork(v8::Local<v8::Function>::Cast(args[1]),
[&args](std::function<void(void*)> complete) {
CreateRequestAndExecute(args[0]->ToObject(), [&args, &complete](const napa::ExecuteRequest& request) {
CreateRequestAndExecute(args[0]->ToObject(), [&args, &complete](const napa::FunctionSpec& spec) {
auto wrap = ObjectWrap::Unwrap<ZoneWrap>(args.Holder());
wrap->_zoneProxy->Execute(request, [complete = std::move(complete)](napa::ExecuteResponse response) {
complete(new napa::ExecuteResponse(std::move(response)));
wrap->_zoneProxy->Execute(spec, [complete = std::move(complete)](napa::Result result) {
complete(new napa::Result(std::move(result)));
});
});
},
[](auto jsCallback, void* result) {
[](auto jsCallback, void* res) {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
auto response = static_cast<napa::ExecuteResponse*>(result);
auto result = static_cast<napa::Result*>(res);
v8::HandleScope scope(isolate);
std::vector<v8::Local<v8::Value>> argv;
argv.emplace_back(CreateResponseObject(*response));
argv.emplace_back(CreateResponseObject(*result));
(void)jsCallback->Call(context, context->Global(), static_cast<int>(argv.size()), argv.data());
delete response;
delete result;
}
);
}
@ -139,17 +139,17 @@ void ZoneWrap::Execute(const v8::FunctionCallbackInfo<v8::Value>& args) {
void ZoneWrap::ExecuteSync(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = v8::Isolate::GetCurrent();
CHECK_ARG(isolate, args[0]->IsObject(), "first argument to zone.execute must be the execution request object");
CHECK_ARG(isolate, args[0]->IsObject(), "first argument to zone.execute must be the function spec object");
CreateRequestAndExecute(args[0]->ToObject(), [&args](const napa::ExecuteRequest& request) {
CreateRequestAndExecute(args[0]->ToObject(), [&args](const napa::FunctionSpec& spec) {
auto wrap = ObjectWrap::Unwrap<ZoneWrap>(args.Holder());
napa::ExecuteResponse response = wrap->_zoneProxy->ExecuteSync(request);
args.GetReturnValue().Set(CreateResponseObject(response));
napa::Result result = wrap->_zoneProxy->ExecuteSync(spec);
args.GetReturnValue().Set(CreateResponseObject(result));
});
}
static v8::Local<v8::Object> CreateResponseObject(const napa::ExecuteResponse& response) {
static v8::Local<v8::Object> CreateResponseObject(const napa::Result& result) {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
@ -158,23 +158,23 @@ static v8::Local<v8::Object> CreateResponseObject(const napa::ExecuteResponse& r
(void)responseObject->CreateDataProperty(
context,
MakeV8String(isolate, "code"),
v8::Uint32::NewFromUnsigned(isolate, response.code));
v8::Uint32::NewFromUnsigned(isolate, result.code));
(void)responseObject->CreateDataProperty(
context,
MakeV8String(isolate, "errorMessage"),
MakeV8String(isolate, response.errorMessage));
MakeV8String(isolate, result.errorMessage));
(void)responseObject->CreateDataProperty(
context,
MakeV8String(isolate, "returnValue"),
MakeV8String(isolate, response.returnValue));
MakeV8String(isolate, result.returnValue));
// Transport context handle
(void)responseObject->CreateDataProperty(
context,
MakeV8String(isolate, "contextHandle"),
PtrToV8Uint32Array(isolate, response.transportContext.release()));
PtrToV8Uint32Array(isolate, result.transportContext.release()));
return responseObject;
}
@ -184,35 +184,35 @@ static void CreateRequestAndExecute(v8::Local<v8::Object> obj, Func&& func) {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
napa::ExecuteRequest request;
napa::FunctionSpec spec;
// module property is optional in a request
// module property is optional in a spec
Utf8String module;
auto maybe = obj->Get(context, MakeV8String(isolate, "module"));
if (!maybe.IsEmpty()) {
module = Utf8String(maybe.ToLocalChecked());
request.module = NAPA_STRING_REF_WITH_SIZE(module.Data(), module.Length());
spec.module = NAPA_STRING_REF_WITH_SIZE(module.Data(), module.Length());
}
// function property is mandatory in a request
// function property is mandatory in a spec
maybe = obj->Get(context, MakeV8String(isolate, "function"));
CHECK_ARG(isolate, !maybe.IsEmpty(), "function property is missing in execution request object");
CHECK_ARG(isolate, !maybe.IsEmpty(), "function property is missing in function spec object");
auto functionValue = maybe.ToLocalChecked();
CHECK_ARG(isolate, functionValue->IsString(), "function property in execution request object must be a string");
CHECK_ARG(isolate, functionValue->IsString(), "function property in function spec object must be a string");
v8::String::Utf8Value function(functionValue->ToString());
request.function = NAPA_STRING_REF_WITH_SIZE(*function, static_cast<size_t>(function.length()));
spec.function = NAPA_STRING_REF_WITH_SIZE(*function, static_cast<size_t>(function.length()));
// arguments are optional in a request
// arguments are optional in a spec
maybe = obj->Get(context, MakeV8String(isolate, "arguments"));
std::vector<Utf8String> arguments;
if (!maybe.IsEmpty()) {
arguments = V8ArrayToVector<Utf8String>(isolate, v8::Local<v8::Array>::Cast(maybe.ToLocalChecked()));
request.arguments.reserve(arguments.size());
spec.arguments.reserve(arguments.size());
for (const auto& arg : arguments) {
request.arguments.emplace_back(NAPA_STRING_REF_WITH_SIZE(arg.Data(), arg.Length()));
spec.arguments.emplace_back(NAPA_STRING_REF_WITH_SIZE(arg.Data(), arg.Length()));
}
}
@ -226,23 +226,23 @@ static void CreateRequestAndExecute(v8::Local<v8::Object> obj, Func&& func) {
// timeout is optional.
maybe = options->Get(context, MakeV8String(isolate, "timeout"));
if (!maybe.IsEmpty()) {
request.options.timeout = maybe.ToLocalChecked()->Uint32Value(context).FromJust();
spec.options.timeout = maybe.ToLocalChecked()->Uint32Value(context).FromJust();
}
// transport option is optional.
maybe = options->Get(context, MakeV8String(isolate, "transport"));
if (!maybe.IsEmpty()) {
request.options.transport = static_cast<napa::TransportOption>(maybe.ToLocalChecked()->Uint32Value(context).FromJust());
spec.options.transport = static_cast<napa::TransportOption>(maybe.ToLocalChecked()->Uint32Value(context).FromJust());
}
}
// transportContext property is mandatory in a request
// transportContext property is mandatory in a spec
maybe = obj->Get(context, MakeV8String(isolate, "transportContext"));
CHECK_ARG(isolate, !maybe.IsEmpty(), "transportContext property is missing in execution request object");
CHECK_ARG(isolate, !maybe.IsEmpty(), "transportContext property is missing in function spec object");
auto transportContextWrap = NAPA_OBJECTWRAP::Unwrap<TransportContextWrapImpl>(maybe.ToLocalChecked()->ToObject());
request.transportContext.reset(transportContextWrap->Get());
spec.transportContext.reset(transportContextWrap->Get());
// Execute
func(request);
func(spec);
}

Просмотреть файл

@ -1,7 +1,7 @@
#include "os.h"
#include <napa-module.h>
#include <napa/module/platform.h>
#include <platform/platform.h>
using namespace napa;
using namespace napa::module;

Просмотреть файл

@ -1,7 +1,7 @@
#include "path.h"
#include <napa-module.h>
#include <napa/module/platform.h>
#include <platform/platform.h>
#include <boost/filesystem.hpp>
#include <string>

Просмотреть файл

@ -1,7 +1,7 @@
#include "process.h"
#include <napa-module.h>
#include <napa/module/platform.h>
#include <platform/platform.h>
#include <boost/filesystem.hpp>
@ -122,8 +122,6 @@ namespace {
}
void HrtimeCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
const static uint32_t NANOS_PER_SECOND = 1000000000;
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
@ -132,30 +130,13 @@ namespace {
uint64_t time = std::chrono::high_resolution_clock::now().time_since_epoch().count();
if (args.Length() == 1) {
CHECK_ARG(isolate, args[0]->IsArray(), "process.hrtime only accepts an Array tuple");
auto arr = v8::Local<v8::Array>::Cast(args[0]);
CHECK_ARG(isolate, arr->Length() == 2, "process.hrtime only accepts an Array tuple of size 2");
uint64_t prev = (static_cast<uint64_t>(arr->Get(0)->Uint32Value()) * NANOS_PER_SECOND)
+ arr->Get(1)->Uint32Value();
auto result = v8_helpers::V8Uint32ArrayToHrtime(isolate, args[0]);
JS_ENSURE(isolate, result.second, "The 1st argument of hrtime must be a two-element uint32 array.");
// Calculate the delta
time -= prev;
time -= result.first;
}
v8::Local<v8::Array> res = v8::Array::New(isolate, 2);
(void)res->CreateDataProperty(
context,
0,
v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(time / NANOS_PER_SECOND)));
(void)res->CreateDataProperty(
context,
1,
v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(time % NANOS_PER_SECOND)));
args.GetReturnValue().Set(res);
args.GetReturnValue().Set(v8_helpers::HrtimeToV8Uint32Array(isolate, time));
}
void UmaskCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {

Просмотреть файл

@ -1,7 +1,7 @@
#include "tty-wrap.h"
#include <napa-module.h>
#include <napa/module/platform.h>
#include <platform/platform.h>
using namespace napa;
using namespace napa::module;

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -1,5 +1,5 @@
#include "module-loader-helpers.h"
#include "core-modules/node/file-system-helpers.h"
#include <module/core-modules/node/file-system-helpers.h>
#include <napa-log.h>
#include <napa/v8-helpers.h>

Просмотреть файл

@ -8,7 +8,10 @@
#include "module-loader-helpers.h"
#include "module-resolver.h"
#include "core-modules/core-modules.h"
#include <module/core-modules/core-modules.h>
// TODO: decouple dependencies between moduler-loader and zone.
#include <zone/worker-context.h>
#include <napa-log.h>
#include <napa-module.h>
@ -104,10 +107,10 @@ private:
};
void ModuleLoader::CreateModuleLoader() {
auto moduleLoader = reinterpret_cast<ModuleLoader*>(WorkerContext::Get(WorkerContextItem::MODULE_LOADER));
auto moduleLoader = reinterpret_cast<ModuleLoader*>(zone::WorkerContext::Get(zone::WorkerContextItem::MODULE_LOADER));
if (moduleLoader == nullptr) {
moduleLoader = new ModuleLoader();
WorkerContext::Set(WorkerContextItem::MODULE_LOADER, moduleLoader);
zone::WorkerContext::Set(zone::WorkerContextItem::MODULE_LOADER, moduleLoader);
// Now, Javascript core module's 'require' can find module loader instance correctly.
moduleLoader->_impl->Bootstrap();
@ -184,7 +187,7 @@ void ModuleLoader::ModuleLoaderImpl::RequireCallback(const v8::FunctionCallbackI
args.Length() == 1 || args.Length() == 2 || args[0]->IsString(),
"Invalid arguments");
auto moduleLoader = reinterpret_cast<ModuleLoader*>(WorkerContext::Get(WorkerContextItem::MODULE_LOADER));
auto moduleLoader = reinterpret_cast<ModuleLoader*>(zone::WorkerContext::Get(zone::WorkerContextItem::MODULE_LOADER));
JS_ENSURE(isolate, moduleLoader != nullptr, "Module loader is not initialized");
v8::String::Utf8Value path(args[0]);
@ -197,7 +200,7 @@ void ModuleLoader::ModuleLoaderImpl::ResolveCallback(const v8::FunctionCallbackI
CHECK_ARG(isolate, args.Length() == 1 && args[0]->IsString(), "Invalid arguments");
auto moduleLoader = reinterpret_cast<ModuleLoader*>(WorkerContext::Get(WorkerContextItem::MODULE_LOADER));
auto moduleLoader = reinterpret_cast<ModuleLoader*>(zone::WorkerContext::Get(zone::WorkerContextItem::MODULE_LOADER));
JS_ENSURE(isolate, moduleLoader != nullptr, "Module loader is not initialized");
v8::String::Utf8Value path(args[0]);
@ -213,7 +216,7 @@ void ModuleLoader::ModuleLoaderImpl::BindingCallback(const v8::FunctionCallbackI
CHECK_ARG(isolate, args.Length() == 1 && args[0]->IsString(), "Invalid arguments");
auto moduleLoader = reinterpret_cast<ModuleLoader*>(WorkerContext::Get(WorkerContextItem::MODULE_LOADER));
auto moduleLoader = reinterpret_cast<ModuleLoader*>(zone::WorkerContext::Get(zone::WorkerContextItem::MODULE_LOADER));
JS_ENSURE(isolate, moduleLoader != nullptr, "Module loader is not initialized");
v8::String::Utf8Value name(args[0]);

Просмотреть файл

Просмотреть файл

@ -1,6 +1,6 @@
#include "module-resolver.h"
#include <napa/module/platform.h>
#include <platform/platform.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>

Просмотреть файл

47
src/module/module.cpp Normal file
Просмотреть файл

@ -0,0 +1,47 @@
#include <napa/module/module-internal.h>
#include <zone/worker-context.h>
using namespace napa;
/// <summary> It sets the persistent constructor at the current V8 isolate. </summary>
/// <param name="name"> Unique constructor name. It's recommended to use the same name as module. </param>
/// <param name="constructor"> V8 persistent function to constructor V8 object. </param>
void napa::module::SetPersistentConstructor(const char* name,
v8::Local<v8::Function> constructor) {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto constructorInfo =
static_cast<ConstructorInfo*>(zone::WorkerContext::Get(zone::WorkerContextItem::CONSTRUCTOR));
if (constructorInfo == nullptr) {
constructorInfo = new ConstructorInfo();
zone::WorkerContext::Set(zone::WorkerContextItem::CONSTRUCTOR, constructorInfo);
}
constructorInfo->constructorMap.emplace(std::piecewise_construct,
std::forward_as_tuple(name),
std::forward_as_tuple(isolate, constructor));
}
/// <summary> It gets the given persistent constructor from the current V8 isolate. </summary>
/// <param name="name"> Unique constructor name given at SetPersistentConstructor() call. </param>
/// <returns> V8 local function object. </returns>
v8::Local<v8::Function> napa::module::GetPersistentConstructor(const char* name) {
auto isolate = v8::Isolate::GetCurrent();
v8::EscapableHandleScope scope(isolate);
auto constructorInfo =
static_cast<ConstructorInfo*>(zone::WorkerContext::Get(zone::WorkerContextItem::CONSTRUCTOR));
if (constructorInfo == nullptr) {
return scope.Escape(v8::Local<v8::Function>());
}
auto iter = constructorInfo->constructorMap.find(name);
if (iter != constructorInfo->constructorMap.end()) {
auto constructor = v8::Local<v8::Function>::New(isolate, iter->second);
return scope.Escape(constructor);
} else {
return scope.Escape(v8::Local<v8::Function>());
}
}

Просмотреть файл

@ -3,7 +3,6 @@
#include <string>
namespace napa {
namespace module {
namespace platform {
/// <summary> Global variable to indicate the number of process arguments. </summary>
@ -44,5 +43,4 @@ namespace platform {
int32_t Isatty(int32_t fd);
} // End of namespce platform.
} // End of namespce module
} // End of namespce napa.

Просмотреть файл

@ -3,7 +3,7 @@
#include <cstdint>
namespace napa {
namespace module {
namespace platform {
namespace tls {
/// <summary> Allocate a thread local storage index. </summary>

Просмотреть файл

@ -1,4 +1,4 @@
#include <napa/module/platform.h>
#include <platform/platform.h>
#include <sstream>
@ -8,7 +8,7 @@
#include <sys/types.h>
#include <windows.h>
using namespace napa::module;
using namespace napa;
int platform::argc = __argc;
char** platform::argv = __argv;

Просмотреть файл

@ -1,7 +1,7 @@
#include <napa/module/thread-local-storage.h>
#include <platform/thread-local-storage.h>
#include <windows.h>
using namespace napa::module;
using namespace napa::platform;
uint32_t tls::Alloc() {
return TlsAlloc();

Просмотреть файл

@ -4,7 +4,7 @@
#include "nop-logging-provider.h"
#include "nop-metric-provider.h"
#include "module/module-resolver.h"
#include <module/loader/module-resolver.h>
#include <napa-log.h>
@ -13,7 +13,7 @@
#include <string>
using namespace napa;
using namespace napa::providers;
// Forward declarations.
@ -25,7 +25,7 @@ static LoggingProvider* _loggingProvider = LoadLoggingProvider("");
static MetricProvider* _metricProvider = LoadMetricProvider("");
bool napa::providers::Initialize(const napa::PlatformSettings& settings) {
bool napa::providers::Initialize(const settings::PlatformSettings& settings) {
_loggingProvider = LoadLoggingProvider(settings.loggingProvider);
_metricProvider = LoadMetricProvider(settings.metricProvider);

Просмотреть файл

@ -10,7 +10,7 @@ namespace napa {
namespace providers {
/// <summary> Initializes and loads all providers based on the provided settings. </summary>
bool Initialize(const napa::PlatformSettings& settings);
bool Initialize(const settings::PlatformSettings& settings);
/// <summary> Clean up and destroy all loaded providers. </summary>
void Shutdown();

Просмотреть файл

@ -1,90 +0,0 @@
#include "execute-task.h"
#include <napa-log.h>
#include <napa/v8-helpers.h>
#include <v8.h>
using namespace napa::scheduler;
using namespace napa::v8_helpers;
napa::scheduler::ExecuteTask::ExecuteTask(const ExecuteRequest& request, ExecuteCallback callback) :
_module(NAPA_STRING_REF_TO_STD_STRING(request.module)),
_func(NAPA_STRING_REF_TO_STD_STRING(request.function)),
_callback(std::move(callback)) {
_args.reserve(request.arguments.size());
for (auto& arg : request.arguments) {
_args.emplace_back(NAPA_STRING_REF_TO_STD_STRING(arg));
}
_options = request.options;
// Pass ownership of the transport context to the execute task.
_transportContext = std::move(request.transportContext);
}
void ExecuteTask::Execute() {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
v8::HandleScope scope(isolate);
// Get the module based main function from global scope.
auto executeFunction = context->Global()->Get(MakeExternalV8String(isolate, "__zone_execute__"));
NAPA_ASSERT(executeFunction->IsFunction(), "__zone_execute__ function must exist in global scope");
// Prepare function args
auto args = v8::Array::New(isolate, static_cast<int>(_args.size()));
for (size_t i = 0; i < _args.size(); ++i) {
(void)args->CreateDataProperty(context, static_cast<uint32_t>(i), MakeExternalV8String(isolate, _args[i]));
}
// Prepare execute options.
// NOTE: export necessary fields from _options to options object here. Now it's empty.
auto options = v8::ObjectTemplate::New(isolate)->NewInstance();
v8::Local<v8::Value> argv[] = {
MakeExternalV8String(isolate, _module),
MakeExternalV8String(isolate, _func),
args,
PtrToV8Uint32Array(isolate, _transportContext.get()),
options
};
// Execute the function.
v8::TryCatch tryCatch(isolate);
auto res = v8::Local<v8::Function>::Cast(executeFunction)->Call(context->Global(), 5, argv);
// Terminating an isolate may occur from a different thread, i.e. from timeout service.
// If the function call already finished successfully when the isolate is terminated it may lead
// to one the following:
// 1. Terminate was called before tryCatch.HasTerminated(), the user gets an error code.
// 2. Terminate was called after tryCatch.HasTerminated(), the user gets a success code.
//
// In both cases the isolate is being restored since this happens before each task executes.
if (tryCatch.HasTerminated()) {
if (_terminationReason == TerminationReason::TIMEOUT) {
LOG_ERROR("Execute", "Task was terminated due to timeout");
_callback({ NAPA_RESPONSE_TIMEOUT, "Execute exceeded timeout", "", std::move(_transportContext) });
} else {
LOG_ERROR("Execute", "Task was terminated for unknown reason");
_callback({ NAPA_RESPONSE_INTERNAL_ERROR, "Execute task terminated", "", std::move(_transportContext) });
}
return;
}
if (tryCatch.HasCaught()) {
auto exception = tryCatch.Exception();
v8::String::Utf8Value exceptionStr(exception);
auto stackTrace = tryCatch.StackTrace();
v8::String::Utf8Value stackTraceStr(stackTrace);
LOG_ERROR("Execute", "JS exception thrown: %s - %s", *exceptionStr, *stackTraceStr);
_callback({ NAPA_RESPONSE_EXECUTE_FUNC_ERROR, *exceptionStr, "", std::move(_transportContext) });
return;
}
v8::String::Utf8Value val(res);
_callback({ NAPA_RESPONSE_SUCCESS, "", std::string(*val, val.length()), std::move(_transportContext) });
}

Просмотреть файл

@ -1,35 +0,0 @@
#pragma once
#include "terminable-task.h"
#include <napa/types.h>
#include <functional>
#include <string>
#include <vector>
namespace napa {
namespace scheduler {
/// <summary> A task for executing pre-loaded javascript functions. </summary>
class ExecuteTask : public TerminableTask {
public:
/// <summary> Constructor. </summary>
/// <param name="request"> The execution request. </param>
/// <param name="callback"> A callback that is triggered when the execute task is completed. </param>
ExecuteTask(const ExecuteRequest& request, ExecuteCallback callback);
/// <summary> Overrides Task.Execute to define execution logic. </summary>
virtual void Execute() override;
private:
std::string _module;
std::string _func;
std::vector<std::string> _args;
ExecuteOptions _options;
ExecuteCallback _callback;
std::unique_ptr<napa::transport::TransportContext> _transportContext;
};
}
}

Просмотреть файл

@ -6,6 +6,7 @@
using namespace boost::program_options;
using namespace napa;
using namespace napa::settings;
static void AddZoneOptions(options_description& desc, ZoneSettings& settings) {
// Zone parsing options should be added here.
@ -26,7 +27,7 @@ static void AddPlatformOptions(options_description& desc, PlatformSettings& sett
("initV8", value(&settings.initV8), "specify whether v8 should be initialized");
}
bool settings_parser::Parse(const std::vector<std::string>& args, ZoneSettings& settings) {
bool settings::Parse(const std::vector<std::string>& args, ZoneSettings& settings) {
options_description desc;
AddZoneOptions(desc, settings);
@ -45,7 +46,7 @@ bool settings_parser::Parse(const std::vector<std::string>& args, ZoneSettings&
return true;
}
bool settings_parser::Parse(const std::vector<std::string>& args, PlatformSettings& settings) {
bool settings::Parse(const std::vector<std::string>& args, PlatformSettings& settings) {
options_description desc;
AddZoneOptions(desc, settings);
AddPlatformOptions(desc, settings);

Просмотреть файл

@ -9,7 +9,7 @@
#include <vector>
namespace napa {
namespace settings_parser {
namespace settings {
/// <summary> Parses napa settings from a vector of arguments. </summary>
/// <param name="str"> The arguments holding the settings. </param>

Просмотреть файл

@ -7,6 +7,7 @@
namespace napa {
namespace settings {
/// <summary> Zone specific settings. </summary>
struct ZoneSettings {
@ -45,4 +46,5 @@ namespace napa {
/// <summary> A flag to specify whether v8 should be initialized. </summary>
bool initV8 = true;
};
}
}

Просмотреть файл

@ -2,8 +2,7 @@
#include <v8.h>
using namespace napa;
using namespace napa::module;
using namespace napa::zone;
AsyncCompleteTask::AsyncCompleteTask(std::shared_ptr<AsyncContext> context) : _context(std::move(context)) {}

Просмотреть файл

@ -2,15 +2,15 @@
#include "async-context.h"
#include <scheduler/task.h>
#include <zone/task.h>
#include <memory>
namespace napa {
namespace module {
namespace zone {
/// <summary> A task to run Javascript callback after asynchronous callback completes. </summary>
class AsyncCompleteTask : public scheduler::Task {
class AsyncCompleteTask : public Task {
public:
/// <summary> Constructor. </summary>

Просмотреть файл

@ -1,26 +1,27 @@
#pragma once
#include <napa/module/napa-async-runner.h>
#include <scheduler/worker.h>
#include <zone/zone-impl.h>
#include <napa/zone/napa-async-runner.h>
#include <zone/worker.h>
#include <zone/napa-zone.h>
#include <v8.h>
#include <future>
namespace napa {
namespace module {
namespace zone {
/// <summary> Class holding asynchonous callbacks. </summary>
struct AsyncContext {
/// <summary> Zone instance issueing asynchronous work. </summary>
ZoneImpl* zone = nullptr;
NapaZone* zone = nullptr;
/// <summary> Keep scheduler instance referenced until async work completes. </summary>
std::shared_ptr<scheduler::Scheduler> scheduler;
std::shared_ptr<zone::Scheduler> scheduler;
/// <summary> Worker Id issueing asynchronous work. </summary>
scheduler::WorkerId workerId;
zone::WorkerId workerId;
/// <summary> Future to wait async callback. </summary>
std::future<void> future;
@ -38,5 +39,5 @@ namespace module {
AsyncCompleteCallback asyncCompleteCallback;
};
} // End of namespace scheduler.
} // End of namespace zone.
} // End of namespace napa.

Просмотреть файл

@ -1,10 +1,11 @@
#include <napa/module/worker-context.h>
#include <napa/module/napa-async-runner.h>
#include <scheduler/async-complete-task.h>
#include <zone/zone-impl.h>
#include <napa/zone/napa-async-runner.h>
#include <zone/async-complete-task.h>
#include <zone/worker-context.h>
#include <zone/napa-zone.h>
using namespace napa;
using namespace napa::module;
using namespace napa::zone;
namespace {
@ -23,9 +24,9 @@ namespace {
/// <param name="jsCallback"> Javascript callback. </summary>
/// <param name="asyncWork"> Function to run asynchronously in separate thread. </param>
/// <param name="asyncCompleteCallback"> Callback running in V8 isolate after asynchronous callback completes. </param>
void napa::module::PostAsyncWork(v8::Local<v8::Function> jsCallback,
AsyncWork asyncWork,
AsyncCompleteCallback asyncCompleteCallback) {
void napa::zone::PostAsyncWork(v8::Local<v8::Function> jsCallback,
AsyncWork asyncWork,
AsyncCompleteCallback asyncCompleteCallback) {
auto context = PrepareAsyncWork(jsCallback, std::move(asyncWork), std::move(asyncCompleteCallback));
if (context == nullptr) {
return;
@ -43,9 +44,9 @@ void napa::module::PostAsyncWork(v8::Local<v8::Function> jsCallback,
/// <param name="jsCallback"> Javascript callback. </summary>
/// <param name="asyncWork"> Function to wrap async-supporting function. </param>
/// <param name="asyncCompleteCallback"> Callback running in V8 isolate after asynchronous function completes. </param>
void napa::module::DoAsyncWork(v8::Local<v8::Function> jsCallback,
const CompletionWork& asyncWork,
AsyncCompleteCallback asyncCompleteCallback) {
void napa::zone::DoAsyncWork(v8::Local<v8::Function> jsCallback,
const CompletionWork& asyncWork,
AsyncCompleteCallback asyncCompleteCallback) {
auto context = PrepareAsyncWork(jsCallback, nullptr, std::move(asyncCompleteCallback));
if (context == nullptr) {
return;
@ -67,16 +68,16 @@ namespace {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto context = std::make_shared<AsyncContext>();
auto context = std::make_shared<zone::AsyncContext>();
context->zone = reinterpret_cast<ZoneImpl*>(WorkerContext::Get(WorkerContextItem::ZONE));
context->zone = reinterpret_cast<NapaZone*>(WorkerContext::Get(WorkerContextItem::ZONE));
if (context->zone == nullptr) {
return nullptr;
}
context->scheduler = context->zone->GetScheduler();
context->workerId = static_cast<scheduler::WorkerId>(
reinterpret_cast<uintptr_t>(module::WorkerContext::Get(WorkerContextItem::WORKER_ID)));
context->workerId = static_cast<WorkerId>(
reinterpret_cast<uintptr_t>(WorkerContext::Get(WorkerContextItem::WORKER_ID)));
context->jsCallback.Reset(isolate, jsCallback);
context->asyncWork = std::move(asyncWork);

87
src/zone/call-context.cpp Normal file
Просмотреть файл

@ -0,0 +1,87 @@
// See: https://groups.google.com/forum/#!topic/nodejs/onA0S01INtw
#ifdef BUILDING_NODE_EXTENSION
#include <node.h>
#endif
#include "call-context.h"
//#include <napa-transport.h>
#include <napa-log.h>
#include <napa/v8-helpers.h>
#include <stdint.h>
using namespace napa::zone;
CallContext::CallContext(const napa::FunctionSpec& spec, napa::ExecuteCallback callback) :
_module(NAPA_STRING_REF_TO_STD_STRING(spec.module)),
_function(NAPA_STRING_REF_TO_STD_STRING(spec.function)),
_callback(callback),
_finished(false) {
// Audit start time.
_startTime = std::chrono::high_resolution_clock::now();
_arguments.reserve(spec.arguments.size());
for (auto& arg : spec.arguments) {
_arguments.emplace_back(NAPA_STRING_REF_TO_STD_STRING(arg));
}
_options = spec.options;
// Pass ownership of the transport context.
_transportContext = std::move(spec.transportContext);
}
bool CallContext::Resolve(std::string marshalledResult) {
auto expected = false;
if (!_finished.compare_exchange_strong(expected, true)) {
return false;
}
_callback({
NAPA_RESULT_SUCCESS,
"",
std::move(marshalledResult),
std::move(_transportContext)
});
return true;
}
bool CallContext::Reject(napa::ResultCode code, std::string reason) {
auto expected = false;
if (!_finished.compare_exchange_strong(expected, true)) {
return false;
}
LOG_ERROR("Execute", "Call was rejected: %s", reason.c_str());
_callback({ code, reason, "", std::move(_transportContext) });
return true;
}
bool CallContext::IsFinished() const {
return _finished;
}
const std::string& CallContext::GetModule() const {
return _module;
}
const std::string& CallContext::GetFunction() const {
return _function;
}
const std::vector<std::string>& CallContext::GetArguments() const {
return _arguments;
}
napa::transport::TransportContext& CallContext::GetTransportContext() {
return *_transportContext.get();
}
const napa::CallOptions& CallContext::GetOptions() const {
return _options;
}
std::chrono::nanoseconds CallContext::GetElapse() const {
return std::chrono::high_resolution_clock::now() - _startTime;
}

80
src/zone/call-context.h Normal file
Просмотреть файл

@ -0,0 +1,80 @@
#pragma once
#include <napa/types.h>
#include <v8.h>
#include <atomic>
#include <chrono>
#include <memory>
#include <vector>
namespace napa {
namespace zone {
/// <summary> Context of calling a JavaScript function. </summary>
class CallContext {
public:
/// <summary> Construct spec from external FunctionSpec. </summary>
explicit CallContext(const napa::FunctionSpec& spec, napa::ExecuteCallback callback);
/// <summary> Resolve current spec. </summary>
/// <param name="result"> marshalled return value. </param>
/// <returns> True if operation is successful, otherwise if task is already finished before. </returns>
//bool Resolve(v8::Local<v8::Value> result);
bool Resolve(std::string result);
/// <summary> Reject current spec. </summary>
/// <param name="resultCode"> Response code to return to user. </summary>
/// <param name="reason"> Reason of cancellation. </summary>
/// <returns> True if operation is successful, otherwise if task is already finished before. </returns>
bool Reject(napa::ResultCode code, std::string reason);
/// <summary> Returns whether current job is completed or cancelled. </summary>
bool IsFinished() const;
/// <summary> Get module name to load function. </summary>
const std::string& GetModule() const;
/// <summary> Get function name to execute. </summary>
const std::string& GetFunction() const;
/// <summary> Get marshalled arguments. </summary>
const std::vector<std::string>& GetArguments() const;
/// <summary> Get transport context. </summary>
napa::transport::TransportContext& GetTransportContext();
/// <summary> Get options. </summary>
const napa::CallOptions& GetOptions() const;
/// <summary> Get elapse since task start in nano-second. </summary>
std::chrono::nanoseconds GetElapse() const;
private:
/// <summary> Module name. </summary>
std::string _module;
/// <summary> Function name. </summary>
std::string _function;
/// <summary> Arguments. </summary>
std::vector<std::string> _arguments;
/// <summary> Execute options. </summary>
napa::CallOptions _options;
/// <summary> Transport context. </summary>
std::unique_ptr<napa::transport::TransportContext> _transportContext;
/// <summary> Callback when task completes. </summary>
napa::ExecuteCallback _callback;
/// <summary> Whether this task is finished. </summary>
std::atomic<bool> _finished;
/// <summary> Call start time. </summary>
std::chrono::high_resolution_clock::time_point _startTime;
};
}
}

51
src/zone/call-task.cpp Normal file
Просмотреть файл

@ -0,0 +1,51 @@
// See: https://groups.google.com/forum/#!topic/nodejs/onA0S01INtw
#ifdef BUILDING_NODE_EXTENSION
#include <node.h>
#endif
#include "call-task.h"
#include <module/core-modules/napa/call-context-wrap.h>
using namespace napa::zone;
using namespace napa::v8_helpers;
napa::zone::CallTask::CallTask(std::shared_ptr<CallContext> context) :
_context(std::move(context)) {
}
void CallTask::Execute() {
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
auto context = isolate->GetCurrentContext();
// Get the module based main function from global scope.
auto executeFunction = context->Global()->Get(MakeExternalV8String(isolate, "__napa_zone_call__"));
NAPA_ASSERT(executeFunction->IsFunction(), "__napa_zone_call__ function must exist in global scope");
// Create task wrap.
auto contextWrap = napa::module::CallContextWrap::NewInstance(_context);
v8::Local<v8::Value> argv[] = { contextWrap };
// Execute the function.
v8::TryCatch tryCatch(isolate);
auto res = v8::Local<v8::Function>::Cast(executeFunction)->Call(context->Global(), 1, argv);
// Terminating an isolate may occur from a different thread, i.e. from timeout service.
// If the function call already finished successfully when the isolate is terminated it may lead
// to one the following:
// 1. Terminate was called before tryCatch.HasTerminated(), the user gets an error code.
// 2. Terminate was called after tryCatch.HasTerminated(), the user gets a success code.
//
// In both cases the isolate is being restored since this happens before each task executes.
if (tryCatch.HasTerminated()) {
if (_terminationReason == TerminationReason::TIMEOUT) {
(void)_context->Reject(NAPA_RESULT_TIMEOUT, "Terminated due to timeout");
} else {
(void)_context->Reject(NAPA_RESULT_INTERNAL_ERROR, "Terminated with unknown reason");
}
return;
}
NAPA_ASSERT(!tryCatch.HasCaught(), "__napa_zone_call__ should catch all user exceptions and reject task.");
}

26
src/zone/call-task.h Normal file
Просмотреть файл

@ -0,0 +1,26 @@
#pragma once
#include "call-context.h"
#include "terminable-task.h"
#include <memory>
namespace napa {
namespace zone {
/// <summary> A task for executing pre-loaded javascript functions. </summary>
class CallTask : public TerminableTask {
public:
/// <summary> Constructor. </summary>
/// <param name="context"> Call context. </param>
CallTask(std::shared_ptr<CallContext> context);
/// <summary> Overrides Task.Execute to define execution logic. </summary>
virtual void Execute() override;
private:
/// <summary> Call context. </summary>
std::shared_ptr<CallContext> _context;
};
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше