Stability: 1 - Experimental
JavaScript implementation of Tiny Actor Run-Time.
The goal of tart
is to provide the smallest possible actor library in JavaScript that has the full power of a "pure" actor model of computation.
tart
happens to fit into a tweet :D
function(){var c=function(b){var a=function(m){setImmediate(function(){x.behavior(m)})},x={self:a,behavior:b,sponsor:c};return a};return c}
Here is a selection of modules from the authors of Tart:
- tart-adapter Adapter turning synchronous functions into asynchronous Tart actors.
- tart-ansible Enables remote message delivery for Tart actors.
- tart-lambda A simple lambda-calculus evaluator.
- tart-marshal Send messages between memory domains (JSON serialization).
- tart-membrane Tart membrane implementation (distributed confinement).
- tart-revocable Tart implementation of a revocable proxy.
- tart-stepping. --DEPRECATED-- superceded by tart-tracing.
- tart-stepping-sandbox Remotely controlled sandbox for controlling tart-stepping.
- tart-tracing Tart tracing control (maintains a full event history for debugging).
- tart-transport-http HTTP transport implementation for Tart.
- tart-transport-https HTTPS transport implementation for Tart.
- tart-transport-tcp TCP transport implementation for Tart.
- tart-transport-tls TLS transport implementation for Tart.
- tart-transport-udp UDP transport implementation for Tart.
- tart-vm Tart behavior sandbox.
To run the below example run:
npm run readme
"use strict";
var tart = require('../index.js');
var sponsor = tart.minimal();
// create an actor that has no state
var statelessActor = sponsor(function (message) {
console.log('got message', message);
});
// create an actor with state
var statefulActorBeh = function (state) {
return function (message) {
console.log('got message', message);
console.log('actor state', state);
};
};
var statefulActor = sponsor(statefulActorBeh({some: 'state'}));
// create an actor with state that changes behavior
var flipFlop = function (state) {
var firstBeh = function (message) {
console.log('firstBeh got message', message);
console.log('actor state', state);
this.behavior = secondBeh;
};
var secondBeh = function (message) {
console.log('secondBeh got message', message);
console.log('actor state', state);
this.behavior = firstBeh;
};
return firstBeh;
};
var serialActor = sponsor(flipFlop({some: 'state'}));
// create an actor that creates a chain of actors
var chainActorBeh = function (count) {
return function (message) {
console.log('chain actor', count);
if (--count >= 0) {
var next = this.sponsor(chainActorBeh(count));
next(message);
}
};
};
var chainActor = sponsor(chainActorBeh(10));
// send messages to the actors
statelessActor('some message');
statefulActor({some: 'other message'});
serialActor('first message');
serialActor('second message');
serialActor('third message');
serialActor('fourth message');
chainActor('go');
npm test
Benchmarks were run on the Minimal implementation.
Erlang Challenge consists of creating a ring of M actors, sending N simple messages around the ring and increasing M until running out of resources.
The benchmark implements a modified version of the challenge by creating 100,000 actors and running 10 simple messages around the ring.
npm run erlangChallenge
starting 100000 actor ring
sending 10 messages
..........
done
all times in NANOSECONDS
construction time:
336492833
loop times:
221762650
212925428
213672756
215595649
215465809
216958143
217696839
221343351
222385758
226308376
loop average:
218411475.9
For rings of sizes larger than 4 Million you may need to expand memory available to V8. To do that, the following command will start the Erlang challenge with ~10GB of memory available:
node --max_old_space_size=10000 scripts/erlangChallenge.js
30 Million actor ring benchmark took up about ~8.5 GB of memory.
starting 30000000 actor ring
sending 1 messages
.
done
all times in NANOSECONDS
construction time:
233098891645
loop times:
244265390009
loop average:
244265390009
The Minimal implementation is the implementation optimized for fastest execution time. In contrast, Pluggable implementation allows for total control of the runtime and execution semantics. Although the default behavior of Pluggable is the same as Minimal, it is somewhat slower due to extra overhead incurred by pluggability of control and observability mechanisms.
Public API
Creates a sponsor capability to create new actors with using the Tweetable implementation :D.
WARNING: If an exception is thrown during message processing the Tweetable run-time will crash. For fastest stable implementation use Minimal.
Same as the core Minimal implementation. See: sponsor(behavior)
Same as the core Minimal implementation. See: actor(message)
Public API
options
: Object (Default:undefined
)fail
: Function (Default:function (exception) {}
)function (exception) {}
An optional handler to call if a sponsored actor behavior throws an exception.
- Return: Function
function (behavior) {}
A capability to create new actors.
Creates a sponsor capability to create new actors with.
var tart = require('tart');
var sponsor = tart.minimal();
var reportingSponsor = tart.minimal({
fail: function (exception) {
console.dir(exception);
}
});
behavior
: Functionfunction (message) {}
Actor behavior to invoke every time an actor receives a message.- Return: Function
function (message) {}
Actor reference in form of a capability that can be invoked to send the actor a message.
Creates a new actor and returns the actor reference in form of a capability to send that actor a message.
var tart = require('tart');
var sponsor = tart.minimal();
var actor = sponsor(function (message) {
console.log('got message', message);
console.log(this.self);
console.log(this.behavior);
console.log(this.sponsor);
});
When the behavior
is invoked upon the receipt of a message, it's this
will be bound with the following:
this.self
: Functionfunction (message) {}
Reference to the actor that is executing thebehavior
(in form of a capability that can be invoked to send the actor a message).this.behavior
: Functionfunction (message) {}
The behavior of the actor. To change actor behavior (a "become" operation) assign a new function to this parameter.this.sponsor
: Functionfunction (behavior) {}
A capability to create new actors. To create a new actor callthis.sponsor(behavior)
.
message
: Any Any message.
Asynchronously sends the message
to the actor
.
var tart = require('tart');
var sponsor = tart.minimal();
var actor = sponsor(function behavior(message) {
console.log('got message', message);
});
actor('hello actor world');
Public API
options
: Object (Default: undefined) Optional overrides.constructConfig
: Function (Default:function (options) {}
)function (options) {}
Configuration creation function that is givenoptions
. It should return a capabilityfunction (behavior) {}
to create new actors.deliver
: Function (Default:function (context, message, options) {}
)function (context, message, options) {}
Deliver function that returns a function fordispatch
to dispatch.dispatch
: Function (Default:setImmediate
)function (deliver) {}
Dispatch function for dispatchingdeliver
closures.fail
: Function (Default:function (exception) {}
)function (exception) {}
An optional handler to call if a sponsored actor behavior throws an exception.annotate
: Function (Default:function (actor) { return actor; }
)function (actor) {}
An optional method to wrap/modify newly-created actors.
- Return: Function
function (behavior) {}
A capability to create new actors.
Creates a sponsor capability to create new actors with and allows replacing parts of the implementation.
To run the below example run:
npm run pluggable
var tart = require('tart');
var dispatch = function (deliver) {
console.log('delivering a message');
deliver();
};
var deliver = function deliver(context, message, options) {
console.log('delivering message', message, 'to context', context);
return function deliver() {
try {
context.behavior(message);
} catch (exception) {
console.log('got exception', exception);
}
};
};
var constructConfig = function constructConfig(options) {
var config = function create(behavior) {
var actor = function send(message) {
options.dispatch(options.deliver(context, message, options));
};
actor = options.annotate(actor);
var context = {
self: actor,
behavior: behavior,
sponsor: config
};
console.log('created actor in context', context);
return actor;
};
return config;
};
var annotate = (function (n) {
return function annotate(actor) {
var id = '@' + n++;
actor.toString = actor.inspect = function () {
return id;
};
return actor;
};
})(0);
var sponsor = tart.pluggable({
constructConfig: constructConfig,
deliver: deliver,
dispatch: dispatch,
annotate: annotate
});
var actor = sponsor(function (message) {
console.log('got message', message);
});
actor('foo');
Same as the core Minimal implementation. See: sponsor(behavior)
Same as the core Minimal implementation. See: actor(message)
We follow semantic versioning policy (semver.org):
Given a version number MAJOR.MINOR.PATCH, increment the:
MAJOR version when you make incompatible API changes,
MINOR version when you add functionality in a backwards-compatible manner, and
PATCH version when you make backwards-compatible bug fixes.