From 133c62fe2e60c8c8a41ede2332644034d3b2ee23 Mon Sep 17 00:00:00 2001 From: dypeng Date: Mon, 7 Aug 2017 16:20:29 -0700 Subject: [PATCH 1/2] 1. Make broadcast/execute args optional. 2. Introduce 'global.napa' to access napajs module. --- docs/api/index.md | 1 + docs/api/napa-globals.md | 20 ++++++++++++++++++++ docs/api/zone.md | 12 ++++++------ lib/index.ts | 5 ++++- lib/zone/zone-impl.ts | 10 +++++++--- lib/zone/zone.ts | 6 +++--- test/memory-test.ts | 6 +++--- test/module-test.ts | 36 ++++++++++++++++++------------------ test/store-test.ts | 4 ++-- test/transport-test.ts | 12 ++++++------ test/zone-test.ts | 10 +++++----- 11 files changed, 75 insertions(+), 47 deletions(-) create mode 100644 docs/api/napa-globals.md diff --git a/docs/api/index.md b/docs/api/index.md index c817eb76..17f699a0 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -1,6 +1,7 @@ # API References ## Core modules +- Napa.js specific [`global`](./napa-globals.md) variables - Namespace [`zone`](./zone.md): Multi-thread JavaScript runtime - Namespace [`transport`](./transport.md): Passing JavaScript values across threads - Namespace [`store`](./store.md): Sharing JavaScript values across threads diff --git a/docs/api/napa-globals.md b/docs/api/napa-globals.md new file mode 100644 index 00000000..897fe3df --- /dev/null +++ b/docs/api/napa-globals.md @@ -0,0 +1,20 @@ +# Napa.js specific global variables + +This file describes Napa.js specific globals, please refer to [this documentation](./node-api.md#globals) for Node.js globals. + +## `global.napa` +Shortcut to access `napajs` module in all Napa enabled isolates. This is helpful to avoid extra `require` when using `napajs` module in anonymous function during `broadcast` or `execute`. + +Example: +```js +var napa = require('napajs'); + +var zone = napa.zone.create('zone1'); + +function test() { + global.napa.log('hi'); +} + +zone.execute(test); + +``` \ No newline at end of file diff --git a/docs/api/zone.md b/docs/api/zone.md index a95bff9c..e30d4e84 100644 --- a/docs/api/zone.md +++ b/docs/api/zone.md @@ -16,9 +16,9 @@ - Interface [`Zone`](#zone) - [`zone.id: string`](#zone-id) - [`zone.broadcast(code: string): Promise`](#broadcast-code) - - [`zone.broadcast(function: (...args: any[]) => void, args: any[]): Promise`](#broadcast-function) - - [`zone.execute(moduleName: string, functionName: string, args: any[], options?: CallOptions): Promise`](#execute-by-name) - - [`zone.execute(function: (...args[]) => any, args: any[], options?: CallOptions): Promise`](#execute-anonymous-function) + - [`zone.broadcast(function: (...args: any[]) => void, args?: any[]): Promise`](#broadcast-function) + - [`zone.execute(moduleName: string, functionName: string, args?: any[], options?: CallOptions): Promise`](#execute-by-name) + - [`zone.execute(function: (...args[]) => any, args?: any[], options?: CallOptions): Promise`](#execute-anonymous-function) - Interface [`CallOptions`](#call-options) - [`options.timeout: number`](#call-options-timeout) - Interface [`Result`](#result) @@ -119,7 +119,7 @@ zone.broadcast('var state = 0;') console.log('broadcast failed.') }); ``` -### zone.broadcast(function: (...args: any[]) => void, args: any[]): Promise\ +### zone.broadcast(function: (...args: any[]) => void, args?: any[]): Promise\ It asynchronously broadcasts an anonymous function with its arguments to all workers, which returns a Promise of void. If any of the workers failed to execute the code, promise will be rejected with an error message. *Please note that Napa doesn't support closure in 'function' during broadcast. @@ -137,7 +137,7 @@ zone.broadcast((state) => { console.log('broadcast failed:', error) }); ``` -### zone.execute(moduleName: string, functionName: string, args: any[], options?: CallOptions): Promise\ +### zone.execute(moduleName: string, functionName: string, args?: any[], options?: CallOptions): Promise\ 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`](#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. @@ -156,7 +156,7 @@ zone.execute( ``` -### zone.execute(function: (...args: any[]) => any, args: any[], options?: CallOptions): Promise\ +### zone.execute(function: (...args: any[]) => any, args?: any[], options?: CallOptions): Promise\ 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`](#result). If error happens, either bad code, user exception, or timeout is reached, promise will be rejected. diff --git a/lib/index.ts b/lib/index.ts index 7eaff0d6..871bf2f8 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -13,4 +13,7 @@ export { log, memory, metric, runtime, store, transport, zone }; // Add execute proxy to global context. import { call } from './zone/function-call'; -((global))["__napa_zone_call__"] = call; \ No newline at end of file +((global))["__napa_zone_call__"] = call; + +// Export 'napa' in global for all isolates that require napajs. +((global))["napa"] = exports; \ No newline at end of file diff --git a/lib/zone/zone-impl.ts b/lib/zone/zone-impl.ts index 65d2af82..ffcc8544 100644 --- a/lib/zone/zone-impl.ts +++ b/lib/zone/zone-impl.ts @@ -12,7 +12,7 @@ interface FunctionSpec { transportContext: transport.TransportContext; } -class ExecuteResult implements zone.Result{ +class Result implements zone.Result{ constructor(payload: string, transportContext: transport.TransportContext) { this._payload = payload; @@ -70,13 +70,13 @@ export class ZoneImpl implements zone.Zone { }); } - public execute(arg1: any, arg2: any, arg3?: any, arg4?: any) : Promise { + public execute(arg1: any, arg2?: any, arg3?: any, arg4?: any) : Promise { let spec : FunctionSpec = this.createExecuteRequest(arg1, arg2, arg3, arg4); return new Promise((resolve, reject) => { this._nativeZone.execute(spec, (result: any) => { if (result.code === 0) { - resolve(new ExecuteResult( + resolve(new Result( result.returnValue, transport.createTransportContext(true, result.contextHandle))); } else { @@ -136,6 +136,10 @@ export class ZoneImpl implements zone.Zone { options = arg4; } + if (args == null) { + args = []; + } + // Create a non-owning transport context which will be passed to execute call. let transportContext: transport.TransportContext = transport.createTransportContext(false); return { diff --git a/lib/zone/zone.ts b/lib/zone/zone.ts index ff913f99..a82d4504 100644 --- a/lib/zone/zone.ts +++ b/lib/zone/zone.ts @@ -97,7 +97,7 @@ export interface Zone { /// The JS function. /// The arguments that will pass to the function. /// A promise which is resolved when broadcast completes and rejected when failed. - broadcast(func: (...args: any[]) => void, args: any[]) : Promise; + broadcast(func: (...args: any[]) => void, args?: any[]) : Promise; /// Executes the function on one of the zone workers. /// The module name that contains the function to execute. @@ -105,13 +105,13 @@ export interface Zone { /// The arguments that will pass to the function. /// Call options, defaults to DEFAULT_CALL_OPTIONS. /// A promise of result which is resolved when execute completes, and rejected when failed. - execute(module: string, func: string, args: any[], options?: CallOptions) : Promise; + execute(module: string, func: string, args?: any[], options?: CallOptions) : Promise; /// Executes the function on one of the zone workers. /// The JS function to execute. /// The arguments that will pass to the function. /// Call options, defaults to DEFAULT_CALL_OPTIONS. /// A promise of result which is resolved when execute completes, and rejected when failed. - execute(func: (...args: any[]) => any, args: any[], options?: CallOptions) : Promise; + execute(func: (...args: any[]) => any, args?: any[], options?: CallOptions) : Promise; } diff --git a/test/memory-test.ts b/test/memory-test.ts index 8be37cff..42fa3e94 100644 --- a/test/memory-test.ts +++ b/test/memory-test.ts @@ -35,7 +35,7 @@ describe('napajs/memory', function() { }); it('@napa: crtAllocator', () => { - napaZone.execute(NAPA_ZONE_TEST_MODULE, "crtAllocatorTest", []); + napaZone.execute(NAPA_ZONE_TEST_MODULE, "crtAllocatorTest"); }); it('@node: defaultAllocator', () => { @@ -45,7 +45,7 @@ describe('napajs/memory', function() { }); it('@napa: defaultAllocator', () => { - napaZone.execute(NAPA_ZONE_TEST_MODULE, "defaultAllocatorTest", []); + napaZone.execute(NAPA_ZONE_TEST_MODULE, "defaultAllocatorTest"); }); it('@node: debugAllocator', () => { @@ -63,7 +63,7 @@ describe('napajs/memory', function() { }); it('@napa: debugAllocator', () => { - napaZone.execute(NAPA_ZONE_TEST_MODULE, "debugAllocatorTest", []); + napaZone.execute(NAPA_ZONE_TEST_MODULE, "debugAllocatorTest"); }); }); }); \ No newline at end of file diff --git a/test/module-test.ts b/test/module-test.ts index 09ba9457..689aae2d 100644 --- a/test/module-test.ts +++ b/test/module-test.ts @@ -71,7 +71,7 @@ describe('napajs/module', function () { describe('resolve', function () { // TODO: support correct __dirname in anonymous function and move tests from 'resolution-tests.js' here. it('require.resolve', () => { - return napaZone.execute(__dirname + "/module/resolution-tests.js", "run", []); + return napaZone.execute(__dirname + "/module/resolution-tests.js", "run"); }); }); @@ -83,7 +83,7 @@ describe('napajs/module', function () { assert(process.argv.length > 0); assert(process.argv[0].includes('node')); - }, []); + }); }); it('execPath', () => { @@ -91,7 +91,7 @@ describe('napajs/module', function () { var assert = require("assert"); assert(process.execPath.includes('node')); - }, []); + }); }); it('env', () => { @@ -100,7 +100,7 @@ describe('napajs/module', function () { process.env.test = "napa-test"; assert.equal(process.env.test, "napa-test"); - }, []); + }); }); it('platform', () => { @@ -111,7 +111,7 @@ describe('napajs/module', function () { process.platform == 'darwin' || process.platform == 'linux' || process.platform == 'freebsd'); - }, []); + }); }); it('umask', () => { @@ -120,7 +120,7 @@ describe('napajs/module', function () { var old = process.umask(0); assert.equal(process.umask(old), 0); - }, []); + }); }); it('chdir', () => { @@ -133,7 +133,7 @@ describe('napajs/module', function () { assert(cwd.includes(process.cwd())); process.chdir(cwd); assert.equal(cwd, process.cwd()); - }, []); + }); }); it('pid', () => { @@ -142,7 +142,7 @@ describe('napajs/module', function () { assert.notEqual(typeof process.pid, undefined); assert(!isNaN(process.pid)); - }, []); + }); }); }); @@ -234,7 +234,7 @@ describe('napajs/module', function () { } else { assert.equal(path.normalize('a\\b\\..\\c/./d/././.'), "a/c/d"); } - }, []); + }); }); it('resolve', () => { @@ -253,7 +253,7 @@ describe('napajs/module', function () { assert.equal(path.resolve("abc", "efg", "../hij", "./xyz.txt"), process.cwd() + "/abc/hij/xyz.txt"); assert.equal(path.resolve("abc", "/a.txt"), "/a.txt"); } - }, []); + }); }); it('join', () => { @@ -266,7 +266,7 @@ describe('napajs/module', function () { } else { assert.equal(path.join("/foo", "bar", "baz/asdf", "quux", ".."), "/foo/bar/baz/asdf"); } - }, []); + }); }); // TODO: fix bugs @@ -286,7 +286,7 @@ describe('napajs/module', function () { assert.equal(path.dirname("/etc"), "/"); assert.equal(path.dirname("/etc/passwd"), "/etc"); } - }, []); + }); }); it('basename', () => { @@ -305,7 +305,7 @@ describe('napajs/module', function () { assert.equal(path.basename("/test/abc.txt", ".txt"), "abc"); assert.equal(path.basename("/windows/abc.txt", ".Txt"), "abc.txt"); } - }, []); + }); }); // TODO: fix bugs @@ -324,7 +324,7 @@ describe('napajs/module', function () { assert.equal(path.extname("/test/a.json.txt"), ".txt"); assert.equal(path.extname("/test/a."), "."); } - }, []); + }); }); it('isAbsolute', () => { @@ -345,7 +345,7 @@ describe('napajs/module', function () { assert.equal(path.isAbsolute("./abc"), false); assert.equal(path.isAbsolute("abc"), false); } - }, []); + }); }); it('relative', () => { @@ -364,7 +364,7 @@ describe('napajs/module', function () { assert.equal(path.relative("/test/a", "a.txt"), "../.." + process.cwd() + "/a.txt"); assert.equal(path.relative("/test/a", "/test/"), ".."); } - }, []); + }); }); it('sep', () => { @@ -377,7 +377,7 @@ describe('napajs/module', function () { } else { assert.equal(path.sep, "/"); } - }, []); + }); }); }); @@ -388,7 +388,7 @@ describe('napajs/module', function () { var os = require("os"); assert(os.type() == "Windows_NT" || os.type() == "Darwin" || os.type() == "Linux"); - }, []); + }); }); }); }); diff --git a/test/store-test.ts b/test/store-test.ts index dec2a841..e76ee111 100644 --- a/test/store-test.ts +++ b/test/store-test.ts @@ -32,7 +32,7 @@ describe('napajs/store', function () { let store2CreationComplete: Promise; it('@napa: store.getOrCreate', () => { - store2CreationComplete = napaZone.execute(NAPA_ZONE_TEST_MODULE, "getOrCreateStoreTest", []); + store2CreationComplete = napaZone.execute(NAPA_ZONE_TEST_MODULE, "getOrCreateStoreTest"); }); it('@node: store.get', async () => { @@ -46,7 +46,7 @@ describe('napajs/store', function () { }); it('@napa: store.get', () => { - napaZone.execute(NAPA_ZONE_TEST_MODULE, "getStoreTest", []); + napaZone.execute(NAPA_ZONE_TEST_MODULE, "getStoreTest"); }); it('simple types: set in node, get in node', () => { diff --git a/test/transport-test.ts b/test/transport-test.ts index fb7d361d..429d77e7 100644 --- a/test/transport-test.ts +++ b/test/transport-test.ts @@ -50,7 +50,7 @@ describe('napajs/transport', () => { }); it('@napa: simple types', () => { - napaZone.execute(NAPA_ZONE_TEST_MODULE, "simpleTypeTransportTest", []); + napaZone.execute(NAPA_ZONE_TEST_MODULE, "simpleTypeTransportTest"); }).timeout(3000); it('@node: JS transportable', () => { @@ -58,7 +58,7 @@ describe('napajs/transport', () => { }); it('@napa: JS transportable', () => { - napaZone.execute(NAPA_ZONE_TEST_MODULE, "jsTransportTest", []); + napaZone.execute(NAPA_ZONE_TEST_MODULE, "jsTransportTest"); }); it('@node: addon transportable', () => { @@ -66,7 +66,7 @@ describe('napajs/transport', () => { }); it('@napa: addon transportable', () => { - napaZone.execute(NAPA_ZONE_TEST_MODULE, "addonTransportTest", []); + napaZone.execute(NAPA_ZONE_TEST_MODULE, "addonTransportTest"); }); it('@node: function transportable', () => { @@ -74,7 +74,7 @@ describe('napajs/transport', () => { }); it('@napa: function transportable', () => { - napaZone.execute(NAPA_ZONE_TEST_MODULE, "functionTransportTest", []); + napaZone.execute(NAPA_ZONE_TEST_MODULE, "functionTransportTest"); }); it('@node: composite transportable', () => { @@ -82,7 +82,7 @@ describe('napajs/transport', () => { }); it('@napa: composite transportable', () => { - napaZone.execute(NAPA_ZONE_TEST_MODULE, "compositeTransportTest", []); + napaZone.execute(NAPA_ZONE_TEST_MODULE, "compositeTransportTest"); }); it('@node: non-transportable', () => { @@ -90,7 +90,7 @@ describe('napajs/transport', () => { }); it('@napa: non-transportable', () => { - napaZone.execute(NAPA_ZONE_TEST_MODULE, "nontransportableTest", []); + napaZone.execute(NAPA_ZONE_TEST_MODULE, "nontransportableTest"); }); }); }); \ No newline at end of file diff --git a/test/zone-test.ts b/test/zone-test.ts index ca0101b4..0eb6c733 100644 --- a/test/zone-test.ts +++ b/test/zone-test.ts @@ -102,7 +102,7 @@ describe('napajs/zone', function () { }); it('@napa', async () => { - let result = await napaZone1.execute(napaZoneTestModule, "getCurrentZone", []); + let result = await napaZone1.execute(napaZoneTestModule, "getCurrentZone"); assert.strictEqual(result.value.id, 'napa-zone1'); }); }); @@ -212,7 +212,7 @@ describe('napajs/zone', function () { return shouldFail(() => { return napa.zone.current.broadcast(() => { console.log(napaZone1.id); - }, []); + }); }); }); @@ -220,7 +220,7 @@ describe('napajs/zone', function () { return shouldFail(() => { return napaZone1.broadcast(() => { console.log(napaZone1.id); - }, []); + }); }); }); @@ -438,12 +438,12 @@ describe('napajs/zone', function () { }); it('@node: -> node zone with anonymous function having closure (should success)', () => { - return napa.zone.current.execute(() => { return napaZone1; }, []); + return napa.zone.current.execute(() => { return napaZone1; }); }); it('@node: -> napa zone with anonymous function having closure (should fail)', () => { return shouldFail(() => { - return napaZone1.execute(() => { return napaZone1; }, []); + return napaZone1.execute(() => { return napaZone1; }); }); }); From c386a69aad73b34e0d4bd30c19ebadc6a3fdcf21 Mon Sep 17 00:00:00 2001 From: dypeng Date: Mon, 7 Aug 2017 16:58:43 -0700 Subject: [PATCH 2/2] Use global.napa in tests --- test/napa-zone/test.ts | 8 +++----- test/zone-test.ts | 15 ++++++--------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/test/napa-zone/test.ts b/test/napa-zone/test.ts index feeb48ae..d7ef5aa3 100644 --- a/test/napa-zone/test.ts +++ b/test/napa-zone/test.ts @@ -4,7 +4,6 @@ import * as assert from 'assert'; import * as path from "path"; import * as napa from '../../lib/index'; -let napaDir: string = path.resolve(__dirname, '../..'); export function bar(input: any) { return input; @@ -95,12 +94,11 @@ export function executeTestFunctionWithTimeout(id: string, waitTimeInMS: number, export function executeWithTransportableArgs(id: string): Promise { let zone = napa.zone.get(id); return new Promise((resolve, reject) => { - zone.execute((allocator: napa.memory.Allocator, napaDir: string) => { + zone.execute((allocator: napa.memory.Allocator) => { var assert = require("assert"); - var napa = require(napaDir); - assert.deepEqual(allocator.handle, napa.memory.crtAllocator.handle); + assert.deepEqual(allocator.handle, (global).napa.memory.crtAllocator.handle); return 1; - }, [napa.memory.crtAllocator, napaDir]) + }, [napa.memory.crtAllocator]) .then ((result: napa.zone.Result) => resolve(result.value)) .catch((error: any) => reject(error)); }); diff --git a/test/zone-test.ts b/test/zone-test.ts index 0eb6c733..386f1c95 100644 --- a/test/zone-test.ts +++ b/test/zone-test.ts @@ -4,7 +4,6 @@ import * as assert from "assert"; import * as path from "path"; import * as napa from "../lib/index"; -let napaDir: string = path.resolve(__dirname, '..'); type Zone = napa.zone.Zone; @@ -460,19 +459,17 @@ describe('napajs/zone', function () { }); it('@node: -> node zone with transportable args', () => { - return napa.zone.current.execute((allocator: napa.memory.Allocator, napaDir: string) => { + return napa.zone.current.execute((allocator: napa.memory.Allocator) => { var assert = require("assert"); - var napa = require(napaDir); - assert.deepEqual(allocator.handle, napa.memory.crtAllocator.handle); - }, [napa.memory.crtAllocator, napaDir]); + assert.deepEqual(allocator.handle, (global).napa.memory.crtAllocator.handle); + }, [napa.memory.crtAllocator]); }); it('@node: -> napa zone with transportable args', () => { - return napaZone1.execute((allocator: napa.memory.Allocator, napaDir: string) => { + return napaZone1.execute((allocator: napa.memory.Allocator) => { var assert = require("assert"); - var napa = require(napaDir); - assert.deepEqual(allocator.handle, napa.memory.crtAllocator.handle); - }, [napa.memory.crtAllocator, napaDir]); + assert.deepEqual(allocator.handle, (global).napa.memory.crtAllocator.handle); + }, [napa.memory.crtAllocator]); }); it('@napa: -> napa zone with transportable args', () => {