git ssb

0+

wanderer🌟 / js-primea-hypervisor



Commit 92f27537381aa9a1deaeb37c51d93bd690be51e4

Merge pull request #116 from primea/port_binding

Port binding
wanderer authored on 5/20/2017, 12:59:56 PM
GitHub committed on 5/20/2017, 12:59:56 PM
Parent: 1f8b8cb2aec1488e4f420467a81021af33fbb92b
Parent: a977e58eb3a89178bf97c0a5b20a7172be8c9b5f

Files changed

index.jschanged
portManager.jschanged
tests/index.jschanged
exoInterface.jsadded
kernel.jsdeleted
index.jsView
@@ -1,65 +1,102 @@
11 const Graph = require('ipld-graph-builder')
2-const Kernel = require('./kernel.js')
2+const ExoInterface = require('./exoInterface.js')
33
44 module.exports = class Hypervisor {
5- constructor (opts) {
6- this.graph = new Graph(opts.dag)
7- this._vmInstances = new Map()
8- this._VMs = {}
5+ /**
6+ * The Hypervisor manages the container instances by instantiating them and
7+ * destorying them when possible. It also facilitates localating Containers
8+ * @param {Graph} dag an instance of [ipfs.dag](https://github.com/ipfs/interface-ipfs-core/tree/master/API/dag#dag-api)
9+ */
10+ constructor (dag) {
11+ this.graph = new Graph(dag)
12+ this._runningContainers = new Map()
13+ this._containerTypes = {}
914 }
1015
11- async getInstance (port, parentPort) {
12- let kernel = this._vmInstances.get(port)
13- if (!kernel) {
14- kernel = await this.createInstance(port.type, port.link, port, parentPort)
15- kernel.on('idle', () => {
16- this._vmInstances.delete(port)
16+ /**
17+ * get a contrainer instance given its entry port and its mounting port
18+ * @param {Object} port the entry port for the container
19+ * @param {Object} parentPort the entry port of the parent container
20+ */
21+ async getOrCreateInstance (port, parentPort) {
22+ let instance = this._runningContainers.get(port)
23+ // if there is no container running crceate one
24+ if (!instance) {
25+ instance = await this.createInstance(port.type, port.link, port, parentPort)
26+ instance.on('idle', () => {
27+ // once the container is done shut it down
28+ this._runningContainers.delete(port)
1729 })
1830 }
19- return kernel
31+ return instance
2032 }
2133
22- // given a port, wait untill its source contract has reached the threshold
23- // tick count
34+ /**
35+ * given a port, wait untill its source contract has reached the threshold
36+ * tick count
37+ * @param {Object} port the port to wait on
38+ * @param {Number} threshold the number of ticks to wait before resolving
39+ * @param {Object} fromPort the entryPort of the container requesting the
40+ * wait. Used internally so that waits don't become cyclic
41+ */
2442 async wait (port, threshold, fromPort) {
25- let kernel = this._vmInstances.get(port)
26- if (kernel) {
27- return kernel.wait(threshold, fromPort)
43+ let instance = this._runningContainers.get(port)
44+ if (instance) {
45+ return instance.wait(threshold, fromPort)
2846 } else {
2947 return threshold
3048 }
3149 }
3250
51+ /**
52+ * creates an instance given the container type, starting state, entry port
53+ * and the parentPort
54+ * @param {String} the type of VM to load
55+ * @param {Object} the starting state of the VM
56+ * @param {Object} the entry port
57+ * @param {Object} the parent port
58+ */
3359 async createInstance (type, state, entryPort = null, parentPort) {
34- const VM = this._VMs[type]
60+ const Container = this._containerTypes[type]
3561
3662 if (!state) {
3763 state = {
38- '/': VM.createState()
64+ '/': Container.createState()
3965 }
4066 }
4167
4268 // create a new kernel instance
43- const kernel = new Kernel({
69+ const exoInterface = new ExoInterface({
4470 entryPort: entryPort,
4571 parentPort: parentPort,
4672 hypervisor: this,
4773 state: state,
48- VM: VM
74+ Container: Container
4975 })
5076
5177 // save the newly created instance
52- this._vmInstances.set(entryPort, kernel)
53- await kernel.start()
54- return kernel
78+ this._runningContainers.set(entryPort, exoInterface)
79+ await exoInterface.start()
80+ return exoInterface
5581 }
5682
83+ /**
84+ * creates a state root starting from a given container and a given number of
85+ * ticks
86+ * @param {Container} container an container instance
87+ * @param {Number} ticks the number of ticks at which to create the state root
88+ */
5789 async createStateRoot (container, ticks) {
5890 await container.wait(ticks)
5991 return this.graph.flush(container.state)
6092 }
6193
94+ /**
95+ * regirsters a container with the hypervisor
96+ * @param {String} the name of the type
97+ * @param {Class} a Class for instantiating the container
98+ */
6299 registerContainer (type, vm) {
63- this._VMs[type] = vm
100+ this._containerTypes[type] = vm
64101 }
65102 }
portManager.jsView
@@ -38,23 +38,30 @@
3838
3939 async start () {
4040 // skip the root, since it doesn't have a parent
4141 if (this.parentPort !== undefined) {
42- this._mapPort(ENTRY, this.parentPort)
42+ this._bindRef(this.parentPort, ENTRY)
4343 }
44+
4445 // map ports to thier id's
4546 this.ports = await this.hypervisor.graph.get(this.state, 'ports')
4647 Object.keys(this.ports).map(name => {
4748 const port = this.ports[name]
48- this._mapPort(name, port)
49+ this._bindRef(port, name)
4950 })
5051 }
5152
52- _mapPort (name, portRef) {
53+ _bindRef (portRef, name) {
5354 const port = new Port(name)
5455 this._portMap.set(portRef, port)
5556 }
5657
58+ bind (port, name) {
59+ // save the port instance
60+ this.ports[name] = port
61+ this._bindRef(port, name)
62+ }
63+
5764 queue (message) {
5865 this._portMap.get(message.fromPort).queue(message)
5966 }
6067
@@ -71,10 +78,10 @@
7178 isValidPort (port) {
7279 return this._portMap.has(port)
7380 }
7481
75- create (type, name) {
76- const VM = this.hypervisor._VMs[type]
82+ create (type) {
83+ const Container = this.hypervisor._containerTypes[type]
7784 const parentId = this.entryPort ? this.entryPort.id : null
7885 let nonce = this.state['/'].nonce
7986
8087 const portRef = {
@@ -86,16 +93,12 @@
8693 }
8794 },
8895 'type': type,
8996 'link': {
90- '/': VM.createState()
97+ '/': Container.createState()
9198 }
9299 }
93100
94- // save the port instance
95- this.ports[name] = portRef
96- this._mapPort(name, portRef)
97-
98101 // incerment the nonce
99102 nonce = new BN(nonce)
100103 nonce.iaddn(1)
101104 this.state['/'].nonce = nonce.toArray()
@@ -118,9 +121,9 @@
118121 }
119122
120123 async getNextMessage () {
121124 if (this._portMap.size) {
122- await this.wait(this.kernel.ticks, this.entryPort)
125+ await this.wait(this.exoInterface.ticks, this.entryPort)
123126 const portMap = [...this._portMap].reduce(messageArbiter)
124127 return portMap[1].shift()
125128 }
126129 }
tests/index.jsView
@@ -41,13 +41,14 @@
4141 t.true(m === message, 'should recive a message')
4242 }
4343 }
4444
45- const hypervisor = new Hypervisor({dag: node.dag})
45+ const hypervisor = new Hypervisor(node.dag)
4646 hypervisor.registerContainer('test', testVMContainer)
4747
4848 const rootContainer = await hypervisor.createInstance('test')
49- const port = await rootContainer.ports.create('test', 'first')
49+ const port = rootContainer.ports.create('test')
50+ rootContainer.ports.bind(port, 'first')
5051
5152 await rootContainer.send(port, message)
5253
5354 const stateRoot = await hypervisor.createStateRoot(rootContainer, Infinity)
@@ -76,21 +77,24 @@
7677 }
7778
7879 class testVMContainer extends BaseContainer {
7980 async run (m) {
80- const port = await this.kernel.ports.create('test2', 'child')
81+ const port = this.kernel.ports.create('test2')
82+ this.kernel.ports.bind(port, 'child')
8183 await this.kernel.send(port, m)
8284 this.kernel.incrementTicks(1)
8385 }
8486 }
8587
86- const hypervisor = new Hypervisor({dag: node.dag})
88+ const hypervisor = new Hypervisor(node.dag)
8789 hypervisor.registerContainer('test', testVMContainer)
8890 hypervisor.registerContainer('test2', testVMContainer2)
8991
9092 let root = await hypervisor.createInstance('test')
91- let port = await root.ports.create('test', 'first')
93+ let port = root.ports.create('test')
9294
95+ root.ports.bind(port, 'first')
96+
9397 await root.send(port, message)
9498 const stateRoot = await hypervisor.createStateRoot(root, Infinity)
9599 t.true(hasResolved, 'should resolve before generating the state root')
96100 t.deepEquals(stateRoot, expectedState, 'expected state')
@@ -105,19 +109,20 @@
105109 }
106110
107111 hypervisor.registerContainer('test', testVMContainer3)
108112 root = await hypervisor.createInstance('test', stateRoot)
109- port = await root.ports.get('first')
113+ port = root.ports.get('first')
110114
111- await root.send(port, message)
115+ root.send(port, message)
112116 })
113117
114118 tape('ping pong', async t => {
115119 class Ping extends BaseContainer {
116120 async run (m) {
117121 let port = this.kernel.ports.get('child')
118122 if (!port) {
119- port = await this.kernel.ports.create('pong', 'child')
123+ port = this.kernel.ports.create('pong')
124+ this.kernel.ports.bind(port, 'child')
120125 }
121126
122127 if (this.kernel.ticks < 100) {
123128 this.kernel.incrementTicks(1)
@@ -133,16 +138,15 @@
133138 return this.kernel.send(port, new Message())
134139 }
135140 }
136141
137- const hypervisor = new Hypervisor({
138- dag: node.dag
139- })
142+ const hypervisor = new Hypervisor(node.dag)
140143
141144 hypervisor.registerContainer('ping', Ping)
142145 hypervisor.registerContainer('pong', Pong)
143146 const root = await hypervisor.createInstance('pong')
144- const port = await root.ports.create('ping', 'child')
147+ const port = root.ports.create('ping')
148+ root.ports.bind(port, 'child')
145149
146150 await root.send(port, new Message())
147151 await hypervisor.createStateRoot(root, Infinity)
148152
@@ -154,12 +158,16 @@
154158 let runs = 0
155159
156160 class Root extends BaseContainer {
157161 async run (m) {
158- const one = this.kernel.ports.create('child', 'one')
159- const two = this.kernel.ports.create('child', 'two')
160- const three = this.kernel.ports.create('child', 'three')
162+ const one = this.kernel.ports.create('child')
163+ const two = this.kernel.ports.create('child')
164+ const three = this.kernel.ports.create('child')
161165
166+ this.kernel.ports.bind(one, 'one')
167+ this.kernel.ports.bind(two, 'two')
168+ this.kernel.ports.bind(three, 'three')
169+
162170 await Promise.all([
163171 this.kernel.send(one, new Message()),
164172 this.kernel.send(two, new Message()),
165173 this.kernel.send(three, new Message())
@@ -180,17 +188,17 @@
180188 this.kernel.incrementTicks(2)
181189 }
182190 }
183191
184- const hypervisor = new Hypervisor({
185- dag: node.dag
186- })
192+ const hypervisor = new Hypervisor(node.dag)
187193
188194 hypervisor.registerContainer('root', Root)
189195 hypervisor.registerContainer('child', Child)
190196
191197 const root = await hypervisor.createInstance('root')
192- const port = await root.ports.create('root', 'first')
198+ const port = root.ports.create('root')
199+ root.ports.bind(port, 'first')
200+
193201 await root.send(port, new Message())
194202 await root.wait(Infinity)
195203
196204 t.equals(runs, 3, 'the number of run should be 3')
@@ -201,19 +209,21 @@
201209 tape('traps', async t => {
202210 t.plan(1)
203211 class Root extends BaseContainer {
204212 async run (m) {
205- this.kernel.ports.create('root', 'one')
206- this.kernel.ports.create('root', 'two')
207- this.kernel.ports.create('root', 'three')
213+ const one = this.kernel.ports.create('child')
214+ const two = this.kernel.ports.create('child')
215+ const three = this.kernel.ports.create('child')
208216
217+ this.kernel.ports.bind(one, 'one')
218+ this.kernel.ports.bind(two, 'two')
219+ this.kernel.ports.bind(three, 'three')
220+
209221 throw new Error('it is a trap!!!')
210222 }
211223 }
212224
213- const hypervisor = new Hypervisor({
214- dag: node.dag
215- })
225+ const hypervisor = new Hypervisor(node.dag)
216226
217227 hypervisor.registerContainer('root', Root)
218228 const root = await hypervisor.createInstance('root')
219229 await root.run()
@@ -229,21 +239,20 @@
229239 tape('invalid port referances', async t => {
230240 t.plan(2)
231241 class Root extends BaseContainer {
232242 async run (m) {
233- const ports = this.kernel.ports.create('root', 'three')
243+ const port = this.kernel.ports.create('root')
244+ this.kernel.ports.bind(port, 'three')
234245 this.kernel.ports.delete('three')
235246 try {
236- await this.kernel.send(ports, new Message())
247+ await this.kernel.send(port, new Message())
237248 } catch (e) {
238249 t.pass()
239250 }
240251 }
241252 }
242253
243- const hypervisor = new Hypervisor({
244- dag: node.dag
245- })
254+ const hypervisor = new Hypervisor(node.dag)
246255
247256 hypervisor.registerContainer('root', Root)
248257 const root = await hypervisor.createInstance('root')
249258 await root.run()
@@ -262,11 +271,14 @@
262271 class Root extends BaseContainer {
263272 async run (m) {
264273 if (!this.runs) {
265274 this.runs = 1
266- const one = this.kernel.ports.create('first', 'one')
267- const two = this.kernel.ports.create('second', 'two')
275+ const one = this.kernel.ports.create('first')
276+ const two = this.kernel.ports.create('second')
268277
278+ this.kernel.ports.bind(one, 'one')
279+ this.kernel.ports.bind(two, 'two')
280+
269281 await Promise.all([
270282 this.kernel.send(one, new Message()),
271283 this.kernel.send(two, new Message())
272284 ])
@@ -294,37 +306,38 @@
294306 await this.kernel.send(m.fromPort, new Message({data: 'second'}))
295307 }
296308 }
297309
298- const hypervisor = new Hypervisor({
299- dag: node.dag
300- })
310+ const hypervisor = new Hypervisor(node.dag)
301311
302312 hypervisor.registerContainer('root', Root)
303313 hypervisor.registerContainer('first', First)
304314 hypervisor.registerContainer('second', Second)
305315
306316 const root = await hypervisor.createInstance('root')
307- const port = await root.ports.create('root', 'first')
308- await root.send(port, new Message())
317+ const port = root.ports.create('root')
318+ root.ports.bind(port, 'first')
319+
320+ root.send(port, new Message())
309321 })
310322
311323 tape('message should arrive in the correct order, even if sent out of order', async t => {
312324 t.plan(2)
313325
314326 class Root extends BaseContainer {
315- async run (m) {
327+ run (m) {
316328 if (!this.runs) {
317329 this.runs = 1
318- const one = this.kernel.ports.create('first', 'one')
319- const two = this.kernel.ports.create('second', 'two')
330+ const one = this.kernel.ports.create('first')
331+ const two = this.kernel.ports.create('second')
320332
321- await Promise.all([
333+ this.kernel.ports.bind(one, 'one')
334+ this.kernel.ports.bind(two, 'two')
335+
336+ return Promise.all([
322337 this.kernel.send(one, new Message()),
323338 this.kernel.send(two, new Message())
324339 ])
325-
326- this.kernel.incrementTicks(6)
327340 } else if (this.runs === 1) {
328341 this.runs++
329342 t.equals(m.data, 'second', 'should recive the first message')
330343 } else if (this.runs === 2) {
@@ -333,32 +346,32 @@
333346 }
334347 }
335348
336349 class First extends BaseContainer {
337- async run (m) {
350+ run (m) {
338351 this.kernel.incrementTicks(2)
339- await this.kernel.send(m.fromPort, new Message({data: 'first'}))
352+ return this.kernel.send(m.fromPort, new Message({data: 'first'}))
340353 }
341354 }
342355
343356 class Second extends BaseContainer {
344- async run (m) {
357+ run (m) {
345358 this.kernel.incrementTicks(1)
346- await this.kernel.send(m.fromPort, new Message({data: 'second'}))
359+ this.kernel.send(m.fromPort, new Message({data: 'second'}))
347360 }
348361 }
349362
350- const hypervisor = new Hypervisor({
351- dag: node.dag
352- })
363+ const hypervisor = new Hypervisor(node.dag)
353364
354365 hypervisor.registerContainer('root', Root)
355366 hypervisor.registerContainer('first', First)
356367 hypervisor.registerContainer('second', Second)
357368
358369 const root = await hypervisor.createInstance('root')
359- const port = await root.ports.create('root', 'first')
360- await root.send(port, new Message())
370+ const port = root.ports.create('root')
371+ root.ports.bind(port, 'first')
372+
373+ root.send(port, new Message())
361374 })
362375
363376 tape('message should arrive in the correct order, even in a tie of ticks', async t => {
364377 t.plan(2)
@@ -366,11 +379,14 @@
366379 class Root extends BaseContainer {
367380 async run (m) {
368381 if (!this.runs) {
369382 this.runs = 1
370- const one = this.kernel.ports.create('first', 'one')
371- const two = this.kernel.ports.create('second', 'two')
383+ const one = this.kernel.ports.create('first')
384+ const two = this.kernel.ports.create('second')
372385
386+ this.kernel.ports.bind(one, 'one')
387+ this.kernel.ports.bind(two, 'two')
388+
373389 await Promise.all([
374390 this.kernel.send(one, new Message()),
375391 this.kernel.send(two, new Message())
376392 ])
@@ -385,50 +401,54 @@
385401 }
386402 }
387403
388404 class First extends BaseContainer {
389- async run (m) {
405+ run (m) {
390406 this.kernel.incrementTicks(2)
391- await this.kernel.send(m.fromPort, new Message({data: 'first'}))
407+ return this.kernel.send(m.fromPort, new Message({
408+ data: 'first'
409+ }))
392410 }
393411 }
394412
395413 class Second extends BaseContainer {
396- async run (m) {
414+ run (m) {
397415 this.kernel.incrementTicks(2)
398- await this.kernel.send(m.fromPort, new Message({data: 'second'}))
416+ return this.kernel.send(m.fromPort, new Message({
417+ data: 'second'
418+ }))
399419 }
400420 }
401421
402- const hypervisor = new Hypervisor({
403- dag: node.dag
404- })
422+ const hypervisor = new Hypervisor(node.dag)
405423
406424 hypervisor.registerContainer('root', Root)
407425 hypervisor.registerContainer('first', First)
408426 hypervisor.registerContainer('second', Second)
409427
410428 const root = await hypervisor.createInstance('root')
411- const port = await root.ports.create('root', 'first')
412- await root.send(port, new Message())
429+ const port = await root.ports.create('root')
430+ root.ports.bind(port, 'first')
431+ root.send(port, new Message())
413432 })
414433
415434 tape('message should arrive in the correct order, even in a tie of ticks', async t => {
416435 t.plan(2)
417436
418437 class Root extends BaseContainer {
419- async run (m) {
438+ run (m) {
420439 if (!this.runs) {
421440 this.runs = 1
422- const two = this.kernel.ports.create('second', 'two')
423- const one = this.kernel.ports.create('first', 'one')
441+ const two = this.kernel.ports.create('second')
442+ const one = this.kernel.ports.create('first')
424443
425- await Promise.all([
444+ this.kernel.ports.bind(two, 'two')
445+ this.kernel.ports.bind(one, 'one')
446+
447+ return Promise.all([
426448 this.kernel.send(two, new Message()),
427449 this.kernel.send(one, new Message())
428450 ])
429-
430- this.kernel.incrementTicks(6)
431451 } else if (this.runs === 1) {
432452 this.runs++
433453 t.equals(m.data, 'first', 'should recived the second message')
434454 } else if (this.runs === 2) {
@@ -437,50 +457,56 @@
437457 }
438458 }
439459
440460 class First extends BaseContainer {
441- async run (m) {
461+ run (m) {
442462 this.kernel.incrementTicks(2)
443- await this.kernel.send(m.fromPort, new Message({data: 'first'}))
463+ return this.kernel.send(m.fromPort, new Message({
464+ data: 'first'
465+ }))
444466 }
445467 }
446468
447469 class Second extends BaseContainer {
448- async run (m) {
470+ run (m) {
449471 this.kernel.incrementTicks(2)
450- await this.kernel.send(m.fromPort, new Message({data: 'second'}))
472+ return this.kernel.send(m.fromPort, new Message({
473+ data: 'second'
474+ }))
451475 }
452476 }
453477
454- const hypervisor = new Hypervisor({
455- dag: node.dag
456- })
478+ const hypervisor = new Hypervisor(node.dag)
457479
458480 hypervisor.registerContainer('root', Root)
459481 hypervisor.registerContainer('first', First)
460482 hypervisor.registerContainer('second', Second)
461483
462484 const root = await hypervisor.createInstance('root')
463- const port = await root.ports.create('root', 'first')
464- await root.send(port, new Message())
485+
486+ const port = root.ports.create('root')
487+ root.ports.bind(port, 'first')
488+
489+ root.send(port, new Message())
465490 })
466491
467492 tape('message should arrive in the correct order, with a tie in ticks but with differnt proity', async t => {
468493 t.plan(2)
469494
470495 class Root extends BaseContainer {
471- async run (m) {
496+ run (m) {
472497 if (!this.runs) {
473498 this.runs = 1
474- const one = this.kernel.ports.create('first', 'one')
475- const two = this.kernel.ports.create('second', 'two')
499+ const one = this.kernel.ports.create('first')
500+ const two = this.kernel.ports.create('second')
476501
477- await Promise.all([
502+ this.kernel.ports.bind(one, 'one')
503+ this.kernel.ports.bind(two, 'two')
504+
505+ return Promise.all([
478506 this.kernel.send(two, new Message()),
479507 this.kernel.send(one, new Message())
480508 ])
481-
482- this.kernel.incrementTicks(6)
483509 } else if (this.runs === 1) {
484510 this.runs++
485511 t.equals(m.data, 'first', 'should recive the first message')
486512 } else if (this.runs === 2) {
@@ -489,11 +515,11 @@
489515 }
490516 }
491517
492518 class First extends BaseContainer {
493- async run (m) {
519+ run (m) {
494520 this.kernel.incrementTicks(2)
495- await this.kernel.send(m.fromPort, new Message({
521+ return this.kernel.send(m.fromPort, new Message({
496522 resources: {
497523 priority: 100
498524 },
499525 data: 'first'
@@ -501,43 +527,46 @@
501527 }
502528 }
503529
504530 class Second extends BaseContainer {
505- async run (m) {
531+ run (m) {
506532 this.kernel.incrementTicks(2)
507- await this.kernel.send(m.fromPort, new Message({data: 'second'}))
533+ return this.kernel.send(m.fromPort, new Message({
534+ data: 'second'
535+ }))
508536 }
509537 }
510538
511- const hypervisor = new Hypervisor({
512- dag: node.dag
513- })
539+ const hypervisor = new Hypervisor(node.dag)
514540
515541 hypervisor.registerContainer('root', Root)
516542 hypervisor.registerContainer('first', First)
517543 hypervisor.registerContainer('second', Second)
518544
519545 const root = await hypervisor.createInstance('root')
520- const port = await root.ports.create('root', 'first')
521- await root.send(port, new Message())
546+ const port = root.ports.create('root')
547+ root.ports.bind(port, 'first')
548+ root.send(port, new Message())
522549 })
523550
524551 tape('message should arrive in the correct order, with a tie in ticks but with differnt proity', async t => {
525552 t.plan(2)
526553
527554 class Root extends BaseContainer {
528- async run (m) {
555+ run (m) {
529556 if (!this.runs) {
530557 this.runs = 1
531- const one = this.kernel.ports.create('first', 'one')
532- const two = this.kernel.ports.create('second', 'two')
533558
534- await Promise.all([
559+ const one = this.kernel.ports.create('first')
560+ const two = this.kernel.ports.create('second')
561+
562+ this.kernel.ports.bind(one, 'one')
563+ this.kernel.ports.bind(two, 'two')
564+
565+ return Promise.all([
535566 this.kernel.send(two, new Message()),
536567 this.kernel.send(one, new Message())
537568 ])
538-
539- this.kernel.incrementTicks(6)
540569 } else if (this.runs === 1) {
541570 this.runs++
542571 t.equals(m.data, 'second', 'should recive the first message')
543572 } else if (this.runs === 2) {
@@ -546,50 +575,52 @@
546575 }
547576 }
548577
549578 class First extends BaseContainer {
550- async run (m) {
579+ run (m) {
551580 this.kernel.incrementTicks(2)
552- await this.kernel.send(m.fromPort, new Message({
581+ return this.kernel.send(m.fromPort, new Message({
553582 data: 'first'
554583 }))
555584 }
556585 }
557586
558587 class Second extends BaseContainer {
559- async run (m) {
588+ run (m) {
560589 this.kernel.incrementTicks(2)
561- await this.kernel.send(m.fromPort, new Message({
590+ return this.kernel.send(m.fromPort, new Message({
562591 resources: {
563592 priority: 100
564593 },
565594 data: 'second'
566595 }))
567596 }
568597 }
569598
570- const hypervisor = new Hypervisor({
571- dag: node.dag
572- })
599+ const hypervisor = new Hypervisor(node.dag)
573600
574601 hypervisor.registerContainer('root', Root)
575602 hypervisor.registerContainer('first', First)
576603 hypervisor.registerContainer('second', Second)
577604
578605 const root = await hypervisor.createInstance('root')
579- const port = await root.ports.create('root', 'first')
580- await root.send(port, new Message())
606+ const port = root.ports.create('root')
607+ root.ports.bind(port, 'first')
608+ root.send(port, new Message())
581609 })
582610
583611 tape('should order parent messages correctly', async t => {
584612 t.plan(1)
585613 class Middle extends BaseContainer {
586- async run (m) {
614+ run (m) {
587615 if (!this.runs) {
588616 this.runs = 1
589617 this.kernel.incrementTicks(1)
590- const leaf = this.kernel.ports.create('leaf', 'leaf')
591- await this.kernel.send(leaf, new Message())
618+
619+ const leaf = this.kernel.ports.create('leaf')
620+ this.kernel.ports.bind(leaf, 'leaf')
621+
622+ return this.kernel.send(leaf, new Message())
592623 } else {
593624 ++this.runs
594625 if (this.runs === 3) {
595626 t.equals(m.data, 'first')
@@ -598,29 +629,28 @@
598629 }
599630 }
600631
601632 class Leaf extends BaseContainer {
602- async run (m) {
633+ run (m) {
603634 this.kernel.incrementTicks(2)
604- await this.kernel.send(m.fromPort, new Message({
635+ return this.kernel.send(m.fromPort, new Message({
605636 data: 'first'
606637 }))
607638 }
608639 }
609640
610- const hypervisor = new Hypervisor({
611- dag: node.dag
612- })
641+ const hypervisor = new Hypervisor(node.dag)
613642
614643 hypervisor.registerContainer('root', BaseContainer)
615644 hypervisor.registerContainer('middle', Middle)
616645 hypervisor.registerContainer('leaf', Leaf)
617646
618647 const root = await hypervisor.createInstance('root')
619648 root.incrementTicks(2)
620649
621- const port = await root.ports.create('middle', 'first')
650+ const port = root.ports.create('middle')
651+ root.ports.bind(port, 'first')
622652
623653 await root.send(port, new Message())
624- await root.send(port, new Message())
654+ root.send(port, new Message())
625655 })
626656 })
exoInterface.jsView
@@ -1,0 +1,145 @@
1+const clearObject = require('object-clear')
2+const clone = require('clone')
3+const EventEmitter = require('events')
4+const PortManager = require('./portManager.js')
5+
6+module.exports = class ExoInterface extends EventEmitter {
7+ /**
8+ * the ExoInterface manages the varous message passing functions and provides
9+ * an interface for the containers to use
10+ * @param {Object} opts
11+ * @param {Object} opts.state
12+ * @param {Object} opts.entryPort
13+ * @param {Object} opts.parentPort
14+ * @param {Object} opts.hypervisor
15+ * @param {Object} opts.Container
16+ */
17+ constructor (opts) {
18+ super()
19+ this.state = opts.state
20+ this.entryPort = opts.entryPort
21+ this.hypervisor = opts.hypervisor
22+
23+ this.containerState = 'idle'
24+ this.ticks = 0
25+
26+ // create the port manager
27+ this.ports = new PortManager(Object.assign({
28+ exoInterface: this
29+ }, opts))
30+
31+ this._waitingMap = new Map()
32+ this.container = new opts.Container(this)
33+
34+ // once we get an result we run the next message
35+ this.on('result', this._runNextMessage)
36+
37+ // on idle clear all the 'wiats'
38+ this.on('idle', () => {
39+ for (const [, waiter] of this._waitingMap) {
40+ waiter.resolve(this.ticks)
41+ }
42+ })
43+ }
44+
45+ start () {
46+ return this.ports.start()
47+ }
48+
49+ queue (message) {
50+ message._hops++
51+ this.ports.queue(message)
52+ if (this.containerState !== 'running') {
53+ this._updateContainerState('running')
54+ this._runNextMessage()
55+ }
56+ }
57+
58+ _updateContainerState (containerState, message) {
59+ this.containerState = containerState
60+ this.emit(containerState, message)
61+ }
62+
63+ async _runNextMessage () {
64+ const message = await this.ports.getNextMessage()
65+ if (message) {
66+ // run the next message
67+ this.run(message)
68+ } else {
69+ // if no more messages then shut down
70+ this._updateContainerState('idle')
71+ }
72+ }
73+
74+ /**
75+ * run the kernels code with a given enviroment
76+ * The Kernel Stores all of its state in the Environment. The Interface is used
77+ * to by the VM to retrive infromation from the Environment.
78+ */
79+ async run (message) {
80+ const oldState = clone(this.state, false, 3)
81+ let result
82+ try {
83+ result = await this.container.run(message) || {}
84+ } catch (e) {
85+ // revert the state
86+ clearObject(this.state)
87+ Object.assign(this.state, oldState)
88+
89+ result = {
90+ exception: true,
91+ exceptionError: e
92+ }
93+ }
94+
95+ this.emit('result', result)
96+ return result
97+ }
98+
99+ // returns a promise that resolves once the kernel hits the threshould tick
100+ // count
101+ wait (threshold, fromPort) {
102+ if (threshold <= this.ticks) {
103+ return this.ticks
104+ } else if (this.containerState === 'idle') {
105+ return this.ports.wait(threshold, fromPort)
106+ } else {
107+ return new Promise((resolve, reject) => {
108+ this._waitingMap.set(fromPort, {
109+ threshold: threshold,
110+ resolve: resolve,
111+ from: fromPort
112+ })
113+ })
114+ }
115+ }
116+
117+ incrementTicks (count) {
118+ this.ticks += count
119+ for (const [fromPort, waiter] of this._waitingMap) {
120+ if (waiter.threshold < this.ticks) {
121+ this._waitingMap.delete(fromPort)
122+ waiter.resolve(this.ticks)
123+ }
124+ }
125+ }
126+
127+ async send (portRef, message) {
128+ if (!this.ports.isValidPort(portRef)) {
129+ throw new Error('invalid port referance')
130+ }
131+
132+ // set the port that the message came from
133+ message._fromPort = this.entryPort
134+ message._fromPortTicks = this.ticks
135+
136+ const instance = await this.hypervisor.getOrCreateInstance(portRef, this.entryPort)
137+ instance.queue(message)
138+
139+ const waiter = this._waitingMap.get(portRef)
140+ if (waiter) {
141+ waiter.resolve(this.ticks)
142+ this._waitingMap.delete(portRef)
143+ }
144+ }
145+}
kernel.jsView
@@ -1,135 +1,0 @@
1-const clearObject = require('object-clear')
2-const clone = require('clone')
3-const EventEmitter = require('events')
4-const PortManager = require('./portManager.js')
5-
6-module.exports = class Kernel extends EventEmitter {
7- constructor (opts) {
8- super()
9- this.state = opts.state
10- this.entryPort = opts.entryPort
11- this.hypervisor = opts.hypervisor
12-
13- this.vmState = 'idle'
14- this.ticks = 0
15-
16- // create the port manager
17- this.ports = new PortManager({
18- kernel: this,
19- hypervisor: opts.hypervisor,
20- state: opts.state,
21- entryPort: opts.entryPort,
22- parentPort: opts.parentPort
23- })
24-
25- this.vm = new opts.VM(this)
26- this._waitingMap = new Map()
27-
28- this.on('result', this._runNextMessage)
29- this.on('idle', () => {
30- for (const [, waiter] of this._waitingMap) {
31- waiter.resolve(this.ticks)
32- }
33- })
34- }
35-
36- start () {
37- return this.ports.start()
38- }
39-
40- queue (message) {
41- message._hops++
42- this.ports.queue(message)
43- if (this.vmState !== 'running') {
44- this._updateVmState('running')
45- this._runNextMessage()
46- }
47- }
48-
49- _updateVmState (vmState, message) {
50- this.vmState = vmState
51- this.emit(vmState, message)
52- }
53-
54- async _runNextMessage () {
55- const message = await this.ports.getNextMessage()
56- if (message) {
57- // run the next message
58- this.run(message)
59- } else {
60- // if no more messages then shut down
61- this._updateVmState('idle')
62- }
63- }
64-
65- /**
66- * run the kernels code with a given enviroment
67- * The Kernel Stores all of its state in the Environment. The Interface is used
68- * to by the VM to retrive infromation from the Environment.
69- */
70- async run (message) {
71- const oldState = clone(this.state, false, 3)
72- let result
73- try {
74- result = await this.vm.run(message) || {}
75- } catch (e) {
76- // revert the state
77- clearObject(this.state)
78- Object.assign(this.state, oldState)
79-
80- result = {
81- exception: true,
82- exceptionError: e
83- }
84- }
85-
86- this.emit('result', result)
87- return result
88- }
89-
90- // returns a promise that resolves once the kernel hits the threshould tick
91- // count
92- wait (threshold, fromPort) {
93- if (threshold <= this.ticks) {
94- return this.ticks
95- } else if (this.vmState === 'idle') {
96- return this.ports.wait(threshold, fromPort)
97- } else {
98- return new Promise((resolve, reject) => {
99- this._waitingMap.set(fromPort, {
100- threshold: threshold,
101- resolve: resolve,
102- from: fromPort
103- })
104- })
105- }
106- }
107-
108- incrementTicks (count) {
109- this.ticks += count
110- for (const [fromPort, waiter] of this._waitingMap) {
111- if (waiter.threshold < this.ticks) {
112- this._waitingMap.delete(fromPort)
113- waiter.resolve(this.ticks)
114- }
115- }
116- }
117-
118- async send (portRef, message) {
119- if (!this.ports.isValidPort(portRef)) {
120- throw new Error('invalid port referance')
121- }
122-
123- message._fromPort = this.entryPort
124- message._fromPortTicks = this.ticks
125-
126- const vm = await this.hypervisor.getInstance(portRef, this.entryPort)
127- vm.queue(message)
128-
129- const waiter = this._waitingMap.get(portRef)
130- if (waiter) {
131- waiter.resolve(this.ticks)
132- this._waitingMap.delete(portRef)
133- }
134- }
135-}

Built with git-ssb-web