Skip to content
This repository has been archived by the owner on Nov 23, 2019. It is now read-only.

worker injection #12

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 77 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,90 @@ Skyrocket
[![Build Status](https://travis-ci.org/runspired/skyrocket.svg)](https://travis-ci.org/runspired/skyrocket)
[![Ember Observer Score](http://emberobserver.com/badges/skyrocket.svg)](http://emberobserver.com/addons/skyrocket)

Skyrocket is an attempt to eliminate the friction of building and using Workers,
unlocking applications that consistently function at 60fps even in the most advanced
use cases in hostile environments. [Read More](./OVERVIEW.md)

- [Terminology Guide](./Terminology.md)

Skyrocket enables you to build multi-threaded applications, helping you create apps
that consistently function at 60fps even in the most advanced use cases in hostile environments.

## Installation

`ember install skyrocket-engine`


## Usage
### Scaffold A Worker

```cli
ember g worker <worker-name>
```

Produces the following:
```cli
/app/workers/<worker-name>/
contract.js
worker.js
```

When building or serving your ember application, `<worker-name>/worker.js` will be built as
as stand alone file located at `/assets/workers/<worker-name>.js`, while
`<worker-name>/contract.js` will be loaded into your app.

### Imports

Your worker can take full advantage of `es6` imports.

### Restrictions

Workers are workers, if a module does something that uses a browser API
that's not available in a worker, it will error. In the near future,
you will be able to import environment stubs to provide your worker needed
browser shims as well.


## Creating Your First Worker

### The Contract

The contract is a "model" of your Worker's exposed API. It represents the
asynchronous methods, events, and properties (called snapshots) which will be available
to your app.

```js
import { Contract, Primitives:p } from 'skyrocket';

export default Interface.extend({
foo: p.snapshot(),
bar: p.method(),
baz: p.event({ outbound: false }), // specify an inbound (to the main thread) only event
spam: p.event({ inbound: false }) // specify an outbound (to the worked) only event
});
```

### The worker

The Worker is the stand alone mini-application that will be created when an Interface is instantiated.
It should implement the primitives that the Interface defines.

```js
import { Worker } from 'skyrocket';
import api from './interface';

export default Worker.extend({
'interface': api,

// foo is a property, but also a snapshot.
// whenever foo updates, it's new state will
// be updated on the app
foo: 'hello world',

// bar can be invoked from app
// it's return can be a value or a promise
bar() {
this.send('baz', {}); // send the baz event to the app
},
onSpam: on('spam', function() {}) // do something when the worker receives the spam event
});
```


<coming soon> See the [Overview](./OVERVIEW.md) for detailed implementation details.
## Using Your Worker


## Fastboot
Expand Down
40 changes: 40 additions & 0 deletions addon/-private/-worker-injection-fill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Ember from 'ember';
import getOwner from 'ember-getowner-polyfill';

const {
inject,
computed
} = Ember;

function lazyInjectWorker(name) {
let [worker, label] = name.split(':');

const fn = function lookupWorkerInstance() {
let owner = getOwner(this);

if (label) {
let instanceName = `worker-instance:${worker}--${label}`;
let instance = owner.lookup(instanceName);

if (!instance) {
let factory = owner._lookupFactory(`worker:${worker}`);

instance = factory.create({
label
});

owner.register(instanceName, instance);
}

return instance;
}

return owner.lookup(worker);
};

return computed(fn);
}

inject.worker = lazyInjectWorker;

export default lazyInjectWorker;
16 changes: 16 additions & 0 deletions addon/-private/-worker-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* WorkerService
*
*
*
* @private
*/
import Ember from 'ember';

const {
Object: Obj
} = Ember;

export default Obj.extend({

});
26 changes: 26 additions & 0 deletions addon/-private/-worker-shell.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Transport from './-worker-transport';

const OWNER = Symbol('-worker-owner');

export default class WorkerShell {

constructor(options) {
this.transport = new Transport({
isWorker: true
});

this.instance = options.worker.create({
[OWNER]: this
});

this.contract = options.contract.create({
transport: this.transport,
instance: this.instance
});
}

static create(options) {
return new WorkerShell(options);
}

}
77 changes: 77 additions & 0 deletions addon/-private/-worker-transport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import Global from './utils/global';

const SYSTEM_TYPE = '-worker';
let REQUEST_INC = 0;

export default class Transport {

constructor(src, options) {
this.src = src || Global;
this.isMaster = !!src;
this.isWorker = !src;
this.options = options;

this.callbacks = new WeakMap();

this.connect();
}

_idFor(request) {
if (!request.__rid) {
request.__rid = `${this.isWorker ? 'W' : 'M'}:${SYSTEM_TYPE}-${Date.now()}-${REQUEST_INC++}`;
}
return request.__rid;
}

connect() {
// send ping
this.src.addEventListener('message', (...args) => {
this._receive(...args);
});
}

registerEvent() {}

registerTask() {}








send() {
this._send({

});
}

sendWithCallback(...args) {
let cb = args.pop();
let req = {
data: args
};
let id = this._idFor(req);

this._registerCallback(id, cb);
this._send(req);
}

_send() {
this.src.postMessage(...arguments);
}

_receive(event) {

}

_registerCallback(id, callback) {
this.callbacks.set(id, {
id,
callback,
startTime: Date.now()
});
}

}
8 changes: 8 additions & 0 deletions addon/-private/object/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
We don't want to use Ember.Object long term,
but this is the easiest way to get this running
at the moment.
*/
import Ember from 'ember';

export default Ember.Object;
6 changes: 6 additions & 0 deletions addon/-private/utils/global.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* global global, self, window */
const GLOBAL_ENV = typeof self !== 'undefined' ? self :
(typeof global !== 'undefined' ? global :
(typeof window !== 'undefined' ? window : false));

export default GLOBAL_ENV;
17 changes: 8 additions & 9 deletions addon/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import Ember from 'ember';
import Interface from './lib/interface';
import Contract from './lib/contract';
import Worker from './lib/worker';
import Primitives from './lib/primitives/index';
import Primitives from 'primitives/index';

export {
Interface,
Worker,
Primitives
Contract,
Primitives,
Worker
};

const Skyrocket = {
Interface,
Worker,
Primitives
Contract,
Primitives,
Worker
};

export default Skyrocket;
5 changes: 5 additions & 0 deletions addon/lib/contract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import SkyrocketObject from '../-private/object/index';

export default SkyrocketObject.extend({
_isContractFactory: true
});
12 changes: 0 additions & 12 deletions addon/lib/interface.js

This file was deleted.

Loading