git ssb

0+

wanderer🌟 / js-primea-hypervisor



Commit ac93636d794481083a1cd4d475925e93a6da01be

fist test passing

wanderer committed on 6/16/2017, 5:18:40 PM
Parent: 3c9cd5e3087d85ea2a475af7ae2f57b4f12e872c

Files changed

exoInterface.jschanged
index.jschanged
package.jsonchanged
portManager.jschanged
scheduler.jschanged
tests/index.jschanged
exoInterface.jsView
@@ -1,9 +1,8 @@
1-const EventEmitter = require('events')
21 const PortManager = require('./portManager.js')
32 const Message = require('primea-message')
43
5-module.exports = class ExoInterface extends EventEmitter {
4+module.exports = class ExoInterface {
65 /**
76 * the ExoInterface manages the varous message passing functions and provides
87 * an interface for the containers to use
98 * @param {Object} opts
@@ -12,9 +11,8 @@
1211 * @param {Object} opts.hypervisor
1312 * @param {Object} opts.Container
1413 */
1514 constructor (opts) {
16- super()
1715 this.state = opts.state
1816 this.hypervisor = opts.hypervisor
1917 this.id = opts.id
2018 this.container = new opts.container.Constructor(this, opts.container.args)
@@ -26,48 +24,47 @@
2624 // create the port manager
2725 this.ports = new PortManager(Object.assign({
2826 exoInterface: this
2927 }, opts))
30-
31- // once we get an result we run the next message
32- this.on('result', this._runNextMessage)
3328 }
3429
3530 /**
3631 * adds a message to this containers message queue
3732 * @param {Message} message
3833 */
3934 queue (portName, message) {
4035 message._hops++
36+ this.ports.addUnboundedPorts(message.ports)
4137 if (this.containerState !== 'running') {
42- this._updateContainerState('running')
38+ this.containerState = 'running'
4339 if (portName) {
4440 this._runNextMessage()
4541 } else {
46- this.run(message, true).then(() => {
47- this._runNextMessage()
48- })
42+ this.run(message, true)
4943 }
5044 }
5145 }
5246
5347 _updateContainerState (containerState, message) {
5448 this.containerState = containerState
55- this.emit(containerState, message)
5649 }
5750
5851 async _runNextMessage () {
59- if (this.ports.hasMessages()) {
60- const message = this.ports.nextMessage()
61- this.ticks = message._ticks
62- this.hypervisor.scheduler.update(this, this.ticks)
63- await this.hypbervisor.scheduler.wait(this.ticks)
64- this.currentMessage = message
65- // run the next message
66- this.run(message)
67- } else {
68- // if no more messages then shut down
69- this._updateContainerState('idle')
52+ try {
53+ if (this.ports.hasMessages()) {
54+ await this.hypervisor.scheduler.wait(this.ticks)
55+ const message = this.ports.nextMessage()
56+ this.ticks = message._ticks
57+ this.hypervisor.scheduler.update(this, this.ticks)
58+ this.currentMessage = message
59+ // run the next message
60+ this.run(message)
61+ } else {
62+ // if no more messages then shut down
63+ this.hypervisor.scheduler.done(this)
64+ }
65+ } catch (e) {
66+ console.log(e)
7067 }
7168 }
7269
7370 /**
@@ -75,22 +72,20 @@
7572 * The Kernel Stores all of its state in the Environment. The Interface is used
7673 * to by the VM to retrive infromation from the Environment.
7774 * @returns {Promise}
7875 */
79- async run (message, init) {
76+ async run (message, init = false) {
8077 let result
78+ const method = init ? 'initailize' : 'run'
8179 try {
82- if (init) {
83- result = await this.container.run(message) || {}
84- } else {
85- result = await this.container.initailize(message) || {}
86- }
80+ result = await this.container[method](message) || {}
8781 } catch (e) {
8882 result = {
8983 exception: true,
9084 exceptionError: e
9185 }
9286 }
87+ this._runNextMessage()
9388 return result
9489 }
9590
9691 /**
index.jsView
@@ -1,20 +1,30 @@
11 const Graph = require('ipld-graph-builder')
2+const Message = require('primea-message')
23 const ExoInterface = require('./exoInterface.js')
3-const Message = require('primea-message')
4+const Scheduler = require('./scheduler.js')
45
56 module.exports = class Hypervisor {
67 /**
78 * The Hypervisor manages the container instances by instantiating them and
89 * destorying them when possible. It also facilitates localating Containers
910 * @param {Graph} dag an instance of [ipfs.dag](https://github.com/ipfs/interface-ipfs-core/tree/master/API/dag#dag-api)
1011 */
1112 constructor (dag, state = {}) {
13+ this.graph = new Graph(dag)
14+ this.scheduler = new Scheduler()
1215 this._state = state
13- this.graph = new Graph(dag)
1416 this._containerTypes = {}
1517 }
1618
19+ getDestPort (port) {
20+ if (port.destPort) {
21+ return port.destPort
22+ } else {
23+ return this.graph.get(this._state, `${port.destId}/ports/${port.destName}`)
24+ }
25+ }
26+
1727 /**
1828 */
1929 async getInstance (id) {
2030 let instance = await this.scheduler.instances.get(id)
@@ -22,47 +32,43 @@
2232 if (!instance) {
2333 const promise = this._loadInstance(id)
2434 this.scheduler.instances.set(id, promise)
2535 instance = await promise
26- instance.once('idle', () => {
27- // once the container is done shut it down
28- this.scheduler.done(instance)
29- })
3036 }
3137 return instance
3238 }
3339
3440 async _loadInstance (id) {
3541 const state = await this.graph.get(this._state, id)
36- const Container = this._containerTypes[state.type]
42+ const container = this._containerTypes[state.type]
3743
3844 // create a new kernel instance
3945 const exoInterface = new ExoInterface({
4046 hypervisor: this,
4147 state: state,
42- Container: Container
48+ container: container,
49+ id: id
4350 })
4451
4552 // save the newly created instance
4653 this.scheduler.update(exoInterface)
4754 return exoInterface
4855 }
4956
50- async createInstance (id, type, code, entryPort) {
57+ async createInstance (type, code, entryPorts = [], id = {nonce: 0, parent: null}) {
58+ id = await this.getHashFromObj(id)
5159 const state = {
52- '/': {
53- nonce: 0,
54- ports: {},
55- type: type,
56- id: {
57- '/': id
58- },
59- code: code
60- }
60+ nonce: [0],
61+ ports: {},
62+ type: type,
63+ code: code
6164 }
65+
6266 await this.graph.set(this._state, id, state)
6367 const exoInterface = await this._loadInstance(id)
64- exoInterface.queue(null, new Message(entryPort))
68+ exoInterface.queue(null, new Message({
69+ ports: entryPorts
70+ }))
6571
6672 return exoInterface
6773 }
6874
@@ -88,5 +94,9 @@
8894 Constructor: Constructor,
8995 args: args
9096 }
9197 }
98+
99+ async getHashFromObj (obj) {
100+ return (await this.graph.flush(obj))['/']
101+ }
92102 }
package.jsonView
@@ -31,9 +31,8 @@
3131 "license": "MPL-2.0",
3232 "dependencies": {
3333 "binary-search-insert": "^1.0.3",
3434 "bn.js": "^4.11.6",
35- "events": "^1.1.1",
3635 "ipld-graph-builder": "1.1.5",
3736 "primea-message": "0.0.1"
3837 },
3938 "devDependencies": {
portManager.jsView
@@ -35,34 +35,36 @@
3535 * @param {Object} opts.exoInterface
3636 */
3737 constructor (opts) {
3838 Object.assign(this, opts)
39- this._unboundPort = new WeakSet()
39+ this.ports = this.state.ports
40+ this._unboundPorts = new WeakSet()
4041 this._waitingPorts = {}
4142 }
4243
44+ addUnboundedPorts (ports) {
45+ ports.forEach(port => {
46+ this._unboundPorts.add(port)
47+ })
48+ }
49+
4350 /**
4451 * binds a port to a name
4552 * @param {Object} port - the port to bind
4653 * @param {String} name - the name of the port
4754 */
48- async bind (port, name) {
55+ async bind (name, port) {
4956 if (this.isBound(port)) {
5057 throw new Error('cannot bind a port that is already bound')
5158 } else if (this.ports[name]) {
5259 throw new Error('cannot bind port to a name that is alread bound')
5360 }
5461
55- let destPort = port.destPort
56- // if the dest is unbound
57- if (destPort) {
58- delete destPort.destPort
59- } else {
60- destPort = await this.hypervisor.getPort(port)
61- }
62+ const destPort = await this.hypervisor.getDestPort(port)
6263
6364 destPort.destName = name
6465 destPort.destId = this.id
66+ delete destPort.destPort
6567
6668 // save the port instance
6769 this.ports[name] = port
6870 }
@@ -81,9 +83,9 @@
8183 if (destPort) {
8284 delete destPort.destName
8385 delete destPort.destId
8486 } else {
85- destPort = await this.hypervisor.getPort(port)
87+ destPort = await this.hypervisor.getDestPort(port)
8688 }
8789 if (del) {
8890 delete destPort.destPort
8991 } else {
@@ -97,9 +99,9 @@
9799 * @param {Object} port
98100 * @return {Boolean}
99101 */
100102 isBound (port) {
101- return !this._unboundPort.has(port)
103+ return !this._unboundPorts.has(port)
102104 }
103105
104106 /**
105107 * queues a message on a port
@@ -128,12 +130,17 @@
128130 * @param {String} type
129131 * @param {*} data - the data to populate the initail state with
130132 * @returns {Promise}
131133 */
132- async create (type, data) {
134+ create (type, data) {
133135 // const container = this.hypervisor._containerTypes[type]
134- let nonce = this.state['/'].nonce
136+ let nonce = this.state.nonce
135137
138+ const id = {
139+ nonce: nonce,
140+ parent: this.id
141+ }
142+
136143 const entryPort = {
137144 messages: []
138145 }
139146
@@ -143,20 +150,15 @@
143150 }
144151
145152 entryPort.destPort = port
146153
147- const id = await this.getIdHash({
148- nonce: nonce,
149- parent: this.id
150- })
154+ this.hypervisor.createInstance(type, data, [entryPort], id)
151155
152- await this.hypervisor.createInstance(id, type, data, entryPort)
153-
154156 // incerment the nonce
155157 nonce = new BN(nonce)
156158 nonce.iaddn(1)
157- this.state['/'].nonce = nonce.toArray()
158- this._unboundPort.add(port)
159+ this.state.nonce = nonce.toArray()
160+ this._unboundPorts.add(port)
159161 return port
160162 }
161163
162164 /**
@@ -189,15 +191,11 @@
189191 * @returns {Promise}
190192 */
191193 nextMessage () {
192194 const portName = Object.keys(this.ports).reduce(messageArbiter)
193- return this.ports[portName].message.shift()
195+ return this.ports[portName].messages.shift()
194196 }
195197
196- hasMessage () {
197- return Object.keys(this.ports).some(name => this.ports[name].message.length)
198+ hasMessages () {
199+ return Object.keys(this.ports).some(name => this.ports[name].messages.length)
198200 }
199-
200- async getIdHash (idObj) {
201- return (await this.graph.flush(idObj))['/']
202- }
203201 }
scheduler.jsView
@@ -10,9 +10,9 @@
1010 this.instances = new Map()
1111 }
1212
1313 update (instance, ticks = this.oldest()) {
14- this.instance.delete(instance.id)
14+ this.instances.delete(instance.id)
1515 const instanceArray = [...this.instances]
1616 binarySearchInsert(instanceArray, comparator, [instance.id, {
1717 ticks: ticks,
1818 instance: instance
@@ -20,11 +20,11 @@
2020 this.instances = new Map(instanceArray)
2121 this._checkWaits()
2222 }
2323
24- done (id) {
25- this._instance.delete(id)
26- if (this._instance.size) {
24+ done (instance) {
25+ this.instances.delete(instance.id)
26+ if (this.instances.size) {
2727 this._checkWaits()
2828 } else {
2929 // clear any remanding waits
3030 this._waits.forEach(wait => {
@@ -34,18 +34,23 @@
3434 }
3535 }
3636
3737 wait (ticks) {
38- return new Promise((resolve, reject) => {
39- binarySearchInsert(this._waits, comparator, {
40- ticks: ticks,
41- resolve: resolve
38+ if (ticks <= this.oldest()) {
39+ return
40+ } else {
41+ return new Promise((resolve, reject) => {
42+ binarySearchInsert(this._waits, comparator, {
43+ ticks: ticks,
44+ resolve: resolve
45+ })
4246 })
43- })
47+ }
4448 }
4549
4650 oldest () {
47- return [...this.instances][0].ticks
51+ const oldest = [...this.instances][0]
52+ return oldest ? oldest[1].ticks : 0
4853 }
4954
5055 _checkWaits () {
5156 const oldest = this.oldest()
tests/index.jsView
@@ -1,61 +1,52 @@
11 const tape = require('tape')
22 const IPFS = require('ipfs')
3-const levelup = require('levelup')
4-const LevelPromise = require('level-promise')
5-const memdown = require('memdown')
63 const Hypervisor = require('../')
74
8-// set up the db
9-const db = levelup('/some/location', {
10- db: memdown
11-})
12-LevelPromise(db)
13-
145 // start ipfs
156 const node = new IPFS({
167 start: false
178 })
189
1910 class BaseContainer {
20- constructor (kernel) {
21- this.kernel = kernel
11+ constructor (exInterface) {
12+ this.exInterface = exInterface
2213 }
23-
24- static createState (code) {
25- return {
26- nonce: [0],
27- ports: {}
28- }
29- }
3014 }
3115
3216 node.on('ready', () => {
33- tape.only('basic', async t => {
17+ tape('basic', async t => {
3418 t.plan(2)
3519 let message
3620 const expectedState = {
37- '/': 'zdpuAntkdU7yBJojcBT5Q9wBhrK56NmLnwpHPKaEGMFnAXpv7'
21+ '/': 'zdpuAyGKaZ3nbBQdgESbEgVYr81TcAFB6LE2MQQPWLZaYxuF3'
3822 }
3923
4024 class testVMContainer extends BaseContainer {
25+ async initailize (message) {
26+ const port = message.ports[0]
27+ if (port) {
28+ await this.exInterface.ports.bind('root', port)
29+ }
30+ }
4131 run (m) {
4232 t.true(m === message, 'should recive a message')
4333 }
4434 }
4535
46- const hypervisor = new Hypervisor(node.dag, db)
36+ const hypervisor = new Hypervisor(node.dag)
4737 hypervisor.registerContainer('test', testVMContainer)
4838
4939 const rootContainer = await hypervisor.createInstance('test')
50- const port = rootContainer.ports.create('test')
40+ const port = await rootContainer.ports.create('test')
5141 message = rootContainer.createMessage()
52- rootContainer.ports.bind(port, 'first')
53-
42+ await rootContainer.ports.bind('first', port)
5443 await rootContainer.send(port, message)
5544
56- const stateRoot = await hypervisor.createStateRoot(rootContainer, Infinity)
57- // t.deepEquals(stateRoot, expectedState, 'expected root!')
45+ const stateRoot = await hypervisor.createStateRoot(Infinity)
46+ // await hypervisor.graph.tree(stateRoot, Infinity)
47+ // console.log(JSON.stringify(stateRoot, null, 2))
48+ t.deepEquals(stateRoot, expectedState, 'expected root!')
5849 })
5950
6051 tape('one child contract', async t => {
6152 t.plan(4)
@@ -65,30 +56,39 @@
6556 }
6657 let hasResolved = false
6758
6859 class testVMContainer2 extends BaseContainer {
60+ async initailize (m) {
61+ await this.exInterface.ports.bind('root', port)
62+ }
6963 run (m) {
7064 t.true(m === message, 'should recive a message 2')
7165 return new Promise((resolve, reject) => {
7266 setTimeout(() => {
73- this.kernel.incrementTicks(1)
67+ this.exInterface.incrementTicks(1)
7468 hasResolved = true
7569 resolve()
7670 }, 200)
7771 })
7872 }
7973 }
8074
8175 class testVMContainer extends BaseContainer {
76+ async initailize (m) {
77+ const port = message.ports[0]
78+ if (port) {
79+ await this.exInterface.ports.bind('root', port)
80+ }
81+ }
8282 async run (m) {
8383 const port = this.kernel.ports.create('test2')
8484 this.kernel.ports.bind(port, 'child')
8585 await this.kernel.send(port, m)
8686 this.kernel.incrementTicks(1)
8787 }
8888 }
8989
90- const hypervisor = new Hypervisor(node.dag, db)
90+ const hypervisor = new Hypervisor(node.dag)
9191 hypervisor.registerContainer('test', testVMContainer)
9292 hypervisor.registerContainer('test2', testVMContainer2)
9393
9494 let root = await hypervisor.createInstance('test')
@@ -117,9 +117,9 @@
117117
118118 root.send(port, message)
119119 })
120120
121- tape('ping pong', async t => {
121+ tape.skip('ping pong', async t => {
122122 class Ping extends BaseContainer {
123123 async run (m) {
124124 let port = this.kernel.ports.get('child')
125125 if (!port) {
@@ -155,9 +155,9 @@
155155
156156 t.end()
157157 })
158158
159- tape('queing multiple messages', async t => {
159+ tape.skip('queing multiple messages', async t => {
160160 t.plan(2)
161161 let runs = 0
162162
163163 class Root extends BaseContainer {
@@ -210,9 +210,9 @@
210210
211211 await hypervisor.graph.tree(root.state, Infinity)
212212 })
213213
214- tape('traps', async t => {
214+ tape.skip('traps', async t => {
215215 t.plan(1)
216216 class Root extends BaseContainer {
217217 async run (m) {
218218 const one = this.kernel.ports.create('child')
@@ -240,9 +240,9 @@
240240 }
241241 }, 'should revert the state')
242242 })
243243
244- tape('message should arrive in the correct oder if sent in order', async t => {
244+ tape.skip('message should arrive in the correct oder if sent in order', async t => {
245245 t.plan(2)
246246
247247 class Root extends BaseContainer {
248248 async run (m) {
@@ -295,9 +295,9 @@
295295
296296 root.send(port, root.createMessage())
297297 })
298298
299- tape('message should arrive in the correct order, even if sent out of order', async t => {
299+ tape.skip('message should arrive in the correct order, even if sent out of order', async t => {
300300 t.plan(2)
301301
302302 class Root extends BaseContainer {
303303 run (m) {
@@ -348,9 +348,9 @@
348348
349349 root.send(port, root.createMessage())
350350 })
351351
352- tape('message should arrive in the correct order, even in a tie of ticks', async t => {
352+ tape.skip('message should arrive in the correct order, even in a tie of ticks', async t => {
353353 t.plan(2)
354354
355355 class Root extends BaseContainer {
356356 async run (m) {
@@ -406,9 +406,9 @@
406406 root.ports.bind(port, 'first')
407407 root.send(port, root.createMessage())
408408 })
409409
410- tape('message should arrive in the correct order, even in a tie of ticks', async t => {
410+ tape.skip('message should arrive in the correct order, even in a tie of ticks', async t => {
411411 t.plan(2)
412412
413413 class Root extends BaseContainer {
414414 run (m) {
@@ -464,9 +464,9 @@
464464
465465 root.send(port, root.createMessage())
466466 })
467467
468- tape('message should arrive in the correct order, with a tie in ticks but with differnt proity', async t => {
468+ tape.skip('message should arrive in the correct order, with a tie in ticks but with differnt proity', async t => {
469469 t.plan(2)
470470
471471 class Root extends BaseContainer {
472472 run (m) {
@@ -523,9 +523,9 @@
523523 root.ports.bind(port, 'first')
524524 root.send(port, root.createMessage())
525525 })
526526
527- tape('message should arrive in the correct order, with a tie in ticks but with differnt proity', async t => {
527+ tape.skip('message should arrive in the correct order, with a tie in ticks but with differnt proity', async t => {
528528 t.plan(2)
529529
530530 class Root extends BaseContainer {
531531 run (m) {
@@ -583,9 +583,9 @@
583583 root.ports.bind(port, 'first')
584584 root.send(port, root.createMessage())
585585 })
586586
587- tape('should order parent messages correctly', async t => {
587+ tape.skip('should order parent messages correctly', async t => {
588588 t.plan(1)
589589 class Middle extends BaseContainer {
590590 run (m) {
591591 if (!this.runs) {
@@ -629,9 +629,9 @@
629629 await root.send(port, root.createMessage())
630630 root.send(port, root.createMessage())
631631 })
632632
633- tape('get container instance by path', async t => {
633+ tape.skip('get container instance by path', async t => {
634634 t.plan(1)
635635 const hypervisor = new Hypervisor(node.dag)
636636 hypervisor.registerContainer('base', BaseContainer)
637637
@@ -651,9 +651,9 @@
651651 const foundThird = await hypervisor.getInstanceByPath(root, 'first/second/third')
652652 t.equals(third, foundThird, 'should find by path')
653653 })
654654
655- tape('checking ports', async t => {
655+ tape.skip('checking ports', async t => {
656656 t.plan(5)
657657 const hypervisor = new Hypervisor(node.dag)
658658 hypervisor.registerContainer('base', BaseContainer)
659659

Built with git-ssb-web