- Introduction
- API
isTransportable(jsValue: any): boolean
register(transportableClass: new(...args: any[]) => any): void
marshall(jsValue: any, context: TransportContext): string
unmarshall(json: string, context: TransporteContext): any
- class
TransportContext
- interface
Transportable
- abstract class
TransportableObject
transportableObject.cid: string
transportableObject.marshall(context: TransportContext): object
transportableObject.unmarshall(payload: object, context: TransportContext): void
- abstract
transportableObject.save(payload: object, context: TransportContext): void
- abstract
transportableObject.load(payload: object, context: TransportContext): void
- decorator
cid
Existing JavaScript engines are not designed for running JavaScript across multiple VMs, which means every VM manages their own heap. Passing values from one VM to another has to be marshalled/unmarshalled. The size of payload and complexity of object will greatly impact communication efficiency. In Napa, we try to work out a design pattern for efficient object sharing, based on the fact that all JavaScript VMs (exposed as workers) reside in the same process, and native objects can be wrapped and exposed as JavaScripts objects.
Following concepts are introduced to implement this pattern:
Transportable types are JavaScript types that can be passed or shared transparently across Napa workers. They are used as value types for passing arguments in zone.broadcast
/ zone.execute
, and sharing objects in key/value pairs via store.set
/ store.get
.
Transportable types are:
- JavaScript primitive types: undefined, null, boolean, number, string
- Object (TypeScript class) that implement
Transportable
interface - Function without referencing closures.
- JavaScript standard built-in objects in this whitelist.
- ArrayBuffer
- Float32Array
- Float64Array
- Int16Array
- Int32Array
- Int8Array
- SharedArrayBuffer
- Uint16Array
- Uint32Array
- Uint8Array
- Array or plain JavaScript object that is composite pattern of above.
For user classes that implement the Transportable
interface, Napa uses Constructor ID (cid
) to lookup constructors for creating a right object from a string payload. cid
is marshalled as a part of the payload. During unmarshalling, the transport layer will extract the cid
, create an object instance using the constructor associated with it, and then call unmarshall on the object.
It's the class developer's responsibility to choose the right cid
for your class. To avoid conflict, we suggest to use the combination of module.id and class name as cid
. Developer can use class decorator cid
to register a user Transportable class automatically, when using TypeScript with decorator feature enabled. Or call transport.register
manually during module initialization.
There are states that cannot be saved or loaded in serialized form (like std::shared_ptr), or it's very inefficient to serialize (like JavaScript function). Transport context is introduced to help in these scenarios. TransportContext objects can be passed from one JavaScript VM to another, or stored in the native world, so lifecycle of shared native objects extended by using TransportContext. An example of the Transportable
implementation using TransportContext is ShareableWrap
.
JavaScript function is a special transportable type, through marshalling its definition into a store, and generate a new function from its definition on target thread.
Highlights on transporting functions are:
- For the same function, marshall/unmarshall is an one-time cost on each JavaScript thread. Once a function is transported for the first time, later transportation of the same function to previous JavaScript thread can be regarded as free.
- Closure cannot be transported, but you won't get an error when transporting a function. Instead, you will get runtime error complaining a variable (from closure) is undefined when you can the function later.
__dirname
/__filename
can be accessed in transported function, which is determined byorigin
property of the function. By default,origin
property is set to the current working directory.
JavaScript standard built-in objects in the whitelist can be transported among napa workers transparently. JavaScript Objects with properties in these types are also able to be transported. Please refer to unit tests for detail.
An example Parallel Quick Sort demonstrated transporting TypedArray (created from SharedArrayBuffer) among multiple Napa workers for efficient data sharing.
It tells whether a JavaScript value is transportable or not.
// JS primitives
assert(transport.isTransportable(undefined));
assert(transport.isTransportable(null));
assert(transport.isTransportable(1));
assert(transport.isTransportable('string'));
assert(transport.isTransportable(true));
// Transportable addon
assert(transport.isTransportable(napa.memory.crtAllocator));
// Composite of transportable types.
assert(transport.isTransportable([
1,
"string",
{ a: napa.memory.crtAllocator }
]));
class B {
field1: number;
field2: string;
}
// Not transportable JS class. (not registered with @cid).
assert(!transport.isTransportable(new B()));
Register a Transportable
class before the transport layer can marshall/unmarshall its instances.
User can also use class decorator @cid
for class registration.
Example:
class A extends transport.AutoTransportable {
field1: string,
method1(): string {
return this.field1;
}
}
// Explicitly register class A in transport.
transport.register(A);
Marshall a transportable JavaScript value into a JSON payload with a TransportContext
.An Error will be thrown if the value is not transportable.
Example:
var context = transport.createTransportContext();
var jsonPayload = transport.marshall(
[1, 'string', napa.memory.crtAllocator],
context);
console.log(jsonPayload);
Unmarshall a transportable JavaScript value from a JSON payload with a TransportContext
.An Error will be thrown if cid
property is found and not registered with the transport layer.
Example:
var value = transport.unmarshall(jsonPayload, context);
Class for Transport Context, that stores shared pointers and functions during marshall/unmarshall.
Save a shareable object in context.
Load a shareable object from handle.
Count of shareable objects saved in the current context.
Interface for the Transportable object.
Get accessor for Constructor ID. It is used to lookup constructor for the payload of the current class.
Marshall transforms this object into a plain JavaScript object with the help of TransportContext.
Unmarshall transforms marshalled payload into current object.
TBD
TBD