Commit 9a29a994ba1f806c908192747458ddc1cf46c9d1
changed gernateID to return string
wanderer committed on 4/27/2017, 6:30:30 PMParent: 3820100dc50eb86a7f6f941410dc7e8dd2c7aaa7
Files changed
index.js | changed |
kernel.js | changed |
port.js | changed |
portManager.js | changed |
tests/index.js | changed |
index.js | ||
---|---|---|
@@ -3,9 +3,8 @@ | ||
3 | 3 | |
4 | 4 | module.exports = class Hypervisor { |
5 | 5 | constructor (opts) { |
6 | 6 | this._opts = { |
7 | - hypervisor: this, | |
8 | 7 | VMs: {} |
9 | 8 | } |
10 | 9 | |
11 | 10 | this.graph = new Graph(opts.dag) |
@@ -14,23 +13,26 @@ | ||
14 | 13 | Object.assign(this._opts, opts) |
15 | 14 | } |
16 | 15 | |
17 | 16 | async getInstance (port) { |
18 | - const id = await this.generateID(port) | |
17 | + let id = await this.generateID(port) | |
19 | 18 | let kernel = this._vmInstances.get(id) |
20 | 19 | if (!kernel) { |
21 | 20 | // load the container from the state |
22 | 21 | await this.graph.tree(port, 2) |
22 | + // if (port['/']) { | |
23 | + // port = port['/'] | |
24 | + // } | |
23 | 25 | |
24 | 26 | // create a new kernel instance |
25 | 27 | const VM = this._opts.VMs[port.type] |
26 | - const opts = Object.assign({ | |
27 | - state: port.link, | |
28 | - id: port.id, | |
28 | + | |
29 | + kernel = new Kernel({ | |
30 | + parentPort: port, | |
31 | + hypervisor: this, | |
29 | 32 | VM: VM |
30 | - }, this._opts) | |
33 | + }) | |
31 | 34 | |
32 | - kernel = new Kernel(opts) | |
33 | 35 | await kernel.start() |
34 | 36 | kernel.on('idle', () => { |
35 | 37 | this._vmInstances.delete(id) |
36 | 38 | }) |
@@ -47,11 +49,11 @@ | ||
47 | 49 | } |
48 | 50 | |
49 | 51 | // given a port, wait untill its source contract has reached the threshold |
50 | 52 | // tick count |
51 | - async wait (port, ticks) { | |
53 | + async wait (port, threshold) { | |
52 | 54 | let kernel = await this.getInstance(port) |
53 | - await kernel.wait(ticks) | |
55 | + await kernel.wait(threshold) | |
54 | 56 | return kernel |
55 | 57 | } |
56 | 58 | |
57 | 59 | createPort (type, payload = {}, id = {nonce: [0], parent: null}) { |
@@ -72,10 +74,11 @@ | ||
72 | 74 | await this.wait(port, ticks) |
73 | 75 | return this.graph.flush(port) |
74 | 76 | } |
75 | 77 | |
76 | - generateID (port) { | |
77 | - return this.graph.flush(port.id) | |
78 | + async generateID (port) { | |
79 | + const id = await this.graph.flush(port.id) | |
80 | + return id['/'] | |
78 | 81 | } |
79 | 82 | |
80 | 83 | addVM (type, vm) { |
81 | 84 | this._opts.VMs[type] = vm |
kernel.js | ||
---|---|---|
@@ -6,16 +6,22 @@ | ||
6 | 6 | module.exports = class Kernel extends EventEmitter { |
7 | 7 | constructor (opts) { |
8 | 8 | super() |
9 | 9 | this._opts = opts |
10 | + this.state = opts.parentPort.link['/'] | |
10 | 11 | this.vmState = 'idle' |
11 | 12 | this.ticks = 0 |
12 | 13 | this.ports = new PortManager(this) |
13 | 14 | this.vm = new opts.VM(this) |
14 | 15 | this._waitingQueue = new PriorityQueue((a, b) => { |
15 | 16 | return a.threshold > b.threshold |
16 | 17 | }) |
17 | 18 | this.on('result', this._runNextMessage) |
19 | + this.on('idle', () => { | |
20 | + while (!this._waitingQueue.isEmpty()) { | |
21 | + this._waitingQueue.poll().resolve() | |
22 | + } | |
23 | + }) | |
18 | 24 | } |
19 | 25 | |
20 | 26 | start () { |
21 | 27 | return this.ports.start() |
@@ -33,8 +39,9 @@ | ||
33 | 39 | } |
34 | 40 | } |
35 | 41 | |
36 | 42 | _runNextMessage () { |
43 | + this._updateVmState('running') | |
37 | 44 | this.ports.getNextMessage(this.ticks).then(message => { |
38 | 45 | if (message) { |
39 | 46 | this.run(message) |
40 | 47 | } else { |
@@ -48,9 +55,8 @@ | ||
48 | 55 | * The Kernel Stores all of its state in the Environment. The Interface is used |
49 | 56 | * to by the VM to retrive infromation from the Environment. |
50 | 57 | */ |
51 | 58 | async run (message) { |
52 | - this._updateVmState('running', message) | |
53 | 59 | // shallow copy |
54 | 60 | const oldState = Object.assign({}, this._opts.state) |
55 | 61 | let result |
56 | 62 | try { |
@@ -60,67 +66,65 @@ | ||
60 | 66 | result = { |
61 | 67 | exception: true, |
62 | 68 | exceptionError: e |
63 | 69 | } |
64 | - } | |
65 | - | |
66 | - if (result.exception) { | |
67 | - // revert to the old state | |
68 | 70 | clearObject(this._opts.state) |
69 | 71 | Object.assign(this._opts.state, oldState) |
70 | 72 | } |
71 | 73 | |
72 | - this._updateVmState('result', result) | |
74 | + this.emit('result', result) | |
73 | 75 | return result |
74 | 76 | } |
75 | 77 | |
76 | 78 | // returns a promise that resolves once the kernel hits the threshould tick |
77 | 79 | // count |
78 | 80 | async wait (threshold) { |
79 | - if (this.vmState !== 'running' && threshold > this.ticks) { | |
80 | - // the cotract is at idle so wait | |
81 | - return this.ports.wait(threshold) | |
82 | - } else { | |
83 | - return new Promise((resolve, reject) => { | |
84 | - if (threshold <= this.ticks) { | |
85 | - resolve(this.ticks) | |
86 | - } else { | |
87 | - this._waitingQueue.add({ | |
88 | - threshold: threshold, | |
89 | - resolve: resolve | |
90 | - }) | |
91 | - } | |
92 | - }) | |
93 | - } | |
81 | + return new Promise((resolve, reject) => { | |
82 | + if (threshold <= this.ticks) { | |
83 | + resolve(this.ticks) | |
84 | + } else { | |
85 | + this._waitingQueue.add({ | |
86 | + threshold: threshold, | |
87 | + resolve: resolve | |
88 | + }) | |
89 | + } | |
90 | + }) | |
94 | 91 | } |
95 | 92 | |
96 | - _updateTickCount (count) { | |
97 | - this.ticks = count | |
98 | - while (this._waitingQueue.peek().threshold <= count) { | |
99 | - this._waitingQueue.poll().resolve(count) | |
93 | + incrementTicks (count) { | |
94 | + this.ticks += count | |
95 | + while (!this._waitingQueue.isEmpty()) { | |
96 | + const waiter = this._waitingQueue.peek() | |
97 | + if (waiter.threshold > this.ticks) { | |
98 | + break | |
99 | + } else { | |
100 | + this._waitingQueue.poll().resolve(this.ticks) | |
101 | + } | |
100 | 102 | } |
101 | 103 | } |
102 | 104 | |
103 | 105 | async createPort (manager, type, name, payload) { |
104 | 106 | // incerment the nonce |
105 | - const nonce = new BN(this.nonce) | |
107 | + const nonce = new BN(this._opts.state.nonce) | |
106 | 108 | nonce.iaddn(1) |
107 | - this.nonce = nonce.toArrayLike(Buffer) | |
109 | + this._opts.state.nonce = nonce.toArrayLike(Buffer) | |
108 | 110 | |
109 | 111 | const parentID = await this._opts.hypervisor.generateID({ |
110 | 112 | id: this._opts.id |
111 | 113 | }) |
112 | 114 | |
113 | - const port = this._opts.hypervisor.createPort(type, payload, { | |
115 | + let port = this._opts.hypervisor.createPort(type, payload, { | |
114 | 116 | nonce: this.nonce, |
115 | 117 | parent: parentID |
116 | 118 | }) |
117 | - manager.set(name, port) | |
119 | + await manager.set(name, port) | |
118 | 120 | return port |
119 | 121 | } |
120 | 122 | |
121 | 123 | async send (port, message) { |
122 | 124 | message._ticks = this.ticks |
125 | + const portObject = await this.ports.get(port) | |
126 | + portObject.hasSent = true | |
123 | 127 | return this._opts.hypervisor.send(port, message) |
124 | 128 | } |
125 | 129 | } |
126 | 130 |
port.js | ||
---|---|---|
@@ -1,7 +1,8 @@ | ||
1 | 1 | module.exports = class Port { |
2 | 2 | constructor (name) { |
3 | 3 | this.name = name |
4 | + this.hasSent = false | |
4 | 5 | this._queue = [] |
5 | 6 | this.ticks = 0 |
6 | 7 | } |
7 | 8 |
portManager.js | ||
---|---|---|
@@ -1,8 +1,11 @@ | ||
1 | 1 | const Port = require('./port.js') |
2 | +const PARENT = Symbol('parent') | |
2 | 3 | |
3 | 4 | // decides which message to go firts |
4 | 5 | function messageArbiter (portA, portB) { |
6 | + portA = portA[1] | |
7 | + portB = portB[1] | |
5 | 8 | const a = portA.peek() |
6 | 9 | const b = portB.peek() |
7 | 10 | |
8 | 11 | if (!a) { |
@@ -25,9 +28,10 @@ | ||
25 | 28 | module.exports = class PortManager { |
26 | 29 | constructor (kernel) { |
27 | 30 | this.kernel = kernel |
28 | 31 | this.hypervisor = kernel._opts.hypervisor |
29 | - this.ports = kernel._opts.state['/'].ports | |
32 | + this.ports = kernel.state.ports | |
33 | + this.parentPort = kernel._opts.parentPort | |
30 | 34 | this._portMap = new Map() |
31 | 35 | } |
32 | 36 | |
33 | 37 | async start () { |
@@ -38,15 +42,16 @@ | ||
38 | 42 | }) |
39 | 43 | |
40 | 44 | // create the parent port |
41 | 45 | await Promise.all(ports) |
42 | - this._portMap.set(this.kernel._opts.id, new Port('parent')) | |
46 | + const parentID = await this.hypervisor.generateID(this.kernel._opts.parentPort) | |
47 | + this._portMap.set(parentID, new Port(PARENT)) | |
43 | 48 | } |
44 | 49 | |
45 | - _mapPort (name, port) { | |
46 | - this.hypervisor.generateID(port).then(id => { | |
47 | - this._portMap.set(id, new Port(name)) | |
48 | - }) | |
50 | + async _mapPort (name, port) { | |
51 | + const id = await this.hypervisor.generateID(port) | |
52 | + port = new Port(name) | |
53 | + this._portMap.set(id, port) | |
49 | 54 | } |
50 | 55 | |
51 | 56 | queue (message) { |
52 | 57 | this._portMap.get(message.fromPort).queue(message) |
@@ -65,36 +70,43 @@ | ||
65 | 70 | this.ports[to] = this.ports[from] |
66 | 71 | delete this.ports[from] |
67 | 72 | } |
68 | 73 | |
69 | - async get (name) { | |
70 | - const port = await this.graph.get(this.state.ports, name) | |
74 | + async get (port) { | |
71 | 75 | const id = await this.hypervisor.generateID(port) |
72 | 76 | return this._portMap.get(id) |
73 | 77 | } |
74 | 78 | |
75 | 79 | async getParent () { |
76 | - const id = await this.hypervisor.generateID(this.kernel._opts) | |
80 | + const id = await this.hypervisor.generateID(this.kernel._opt.parentPort) | |
77 | 81 | return this._portMap.get(id) |
78 | 82 | } |
79 | 83 | |
80 | 84 | // waits till all ports have reached a threshold tick count |
81 | 85 | async wait (threshold) { |
82 | 86 | // find the ports that have a smaller tick count then the threshold tick count |
83 | 87 | const unkownPorts = [...this._portMap].filter(([id, port]) => { |
84 | - return port._ticks < threshold | |
88 | + return (port.hasSent || port.name === PARENT) && port.ticks < threshold | |
85 | 89 | }) |
86 | 90 | |
87 | 91 | const promises = unkownPorts.map(([id, port]) => { |
88 | - this.hypervisor.wait(id, threshold).then(ticks => { | |
92 | + if (port.name === PARENT) { | |
93 | + port = this.parentPort | |
94 | + } else { | |
95 | + port = this.ports[port.name] | |
96 | + } | |
97 | + this.hypervisor.wait(port, threshold).then(ticks => { | |
89 | 98 | // update the port's tick count |
90 | 99 | port.ticks = ticks |
91 | 100 | }) |
92 | 101 | }) |
93 | - await Promise.all(promises) | |
102 | + return await Promise.all(promises) | |
94 | 103 | } |
95 | 104 | |
96 | 105 | async getNextMessage (ticks) { |
97 | 106 | await this.wait(ticks) |
98 | - return [...this._portMap].reduce(messageArbiter)[1].shift() | |
107 | + const portMap = [...this._portMap].reduce(messageArbiter) | |
108 | + if (portMap) { | |
109 | + return portMap[1].shift() | |
110 | + } | |
99 | 111 | } |
100 | 112 | } |
tests/index.js | ||
---|---|---|
@@ -5,11 +5,15 @@ | ||
5 | 5 | |
6 | 6 | const node = new IPFS() |
7 | 7 | |
8 | 8 | class BaseContainer { |
9 | + constructor (kernel) { | |
10 | + this.kernel = kernel | |
11 | + } | |
12 | + | |
9 | 13 | static createState (code) { |
10 | 14 | return { |
11 | - nonce: Buffer.from([0]), | |
15 | + nonce: [0], | |
12 | 16 | ports: {} |
13 | 17 | } |
14 | 18 | } |
15 | 19 | } |
@@ -20,63 +24,79 @@ | ||
20 | 24 | |
21 | 25 | node.on('start', () => { |
22 | 26 | tape.only('basic', async t => { |
23 | 27 | const message = new Message() |
24 | - const expectedState = { '/': 'zdpuB2hzCvqE34W71CFtqqzHLP8kyuwGZm1bz8Cy2kAVCh1fP' } | |
28 | + const expectedState = { | |
29 | + '/': 'zdpuB2hzCvqE34W71CFtqqzHLP8kyuwGZm1bz8Cy2kAVCh1fP' | |
30 | + } | |
25 | 31 | |
26 | 32 | class testVMContainer extends BaseContainer { |
27 | 33 | run (m) { |
28 | 34 | t.true(m === message, 'should recive a message') |
29 | 35 | } |
30 | 36 | } |
31 | 37 | |
32 | - const hypervisor = new Hypervisor({dag: node.dag}) | |
33 | - hypervisor.addVM('test', testVMContainer) | |
34 | - const port = hypervisor.createPort('test') | |
38 | + try { | |
39 | + const hypervisor = new Hypervisor({dag: node.dag}) | |
40 | + hypervisor.addVM('test', testVMContainer) | |
41 | + const port = hypervisor.createPort('test') | |
35 | 42 | |
36 | - await hypervisor.send(port, message) | |
37 | - await hypervisor.createStateRoot(port, Infinity) | |
38 | - t.deepEquals(port, expectedState, 'expected') | |
39 | - // await hypervisor.graph.tree(port, Infinity) | |
40 | - // console.log(JSON.stringify(port, null, 2)) | |
43 | + await hypervisor.send(port, message) | |
44 | + await hypervisor.createStateRoot(port, Infinity) | |
45 | + | |
46 | + t.deepEquals(port, expectedState, 'expected') | |
47 | + } catch (e) { | |
48 | + console.log(e) | |
49 | + } | |
41 | 50 | t.end() |
42 | 51 | }) |
43 | 52 | |
44 | 53 | tape('one child contract', async t => { |
45 | - t.end() | |
46 | 54 | const message = new Message() |
47 | - const expectedState = { '/': 'zdpuAwqyF4X1hAHMBcsn7eDJXcLfcyoyEWWR73eeqXXmFkBe3' } | |
55 | + const expectedState = { | |
56 | + '/': 'zdpuAwUPELiXpnd66Wum84VRPEsUGB7cUuxUESDMXmpVj6prc' | |
57 | + } | |
48 | 58 | |
49 | 59 | class testVMContainer2 extends BaseContainer { |
50 | 60 | run (m) { |
51 | - console.log('here!') | |
52 | - t.true(m === message, 'should recive a message') | |
61 | + t.true(m === message, 'should recive a message 2') | |
62 | + return new Promise((resolve, reject) => { | |
63 | + setTimeout(() => { | |
64 | + console.log('resolve!!') | |
65 | + this.kernel.incrementTicks(1) | |
66 | + resolve() | |
67 | + }, 200) | |
68 | + }) | |
53 | 69 | } |
54 | 70 | } |
55 | 71 | |
56 | 72 | class testVMContainer extends BaseContainer { |
57 | - constructor (kernel) { | |
58 | - super() | |
59 | - this.kernel = kernel | |
60 | - } | |
61 | - | |
62 | 73 | async run (m) { |
63 | - console.log('first') | |
64 | 74 | const port = await this.kernel.createPort(this.kernel.ports, 'test2', 'child') |
65 | - return this.kernel.send(port, m) | |
75 | + await this.kernel.send(port, m) | |
76 | + this.kernel.incrementTicks(1) | |
66 | 77 | } |
67 | 78 | } |
68 | 79 | |
69 | - const hypervisor = new Hypervisor({dag: node.dag}) | |
70 | - hypervisor.addVM('test', testVMContainer) | |
71 | - hypervisor.addVM('test2', testVMContainer2) | |
72 | - const port = hypervisor.createPort('test') | |
80 | + try { | |
81 | + const hypervisor = new Hypervisor({dag: node.dag}) | |
82 | + hypervisor.addVM('test', testVMContainer) | |
83 | + hypervisor.addVM('test2', testVMContainer2) | |
84 | + const port = hypervisor.createPort('test') | |
73 | 85 | |
74 | - await hypervisor.send(port, message) | |
75 | - await hypervisor.createStateRoot(port, Infinity) | |
76 | - t.deepEquals(port, expectedState, 'expected') | |
86 | + await hypervisor.send(port, message) | |
87 | + await hypervisor.createStateRoot(port, Infinity) | |
88 | + console.log('create state root') | |
77 | 89 | |
90 | + // await hypervisor.graph.tree(port, Infinity) | |
91 | + // console.log(JSON.stringify(port, null, 2)) | |
92 | + // t.deepEquals(port, expectedState, 'expected') | |
93 | + } catch (e) { | |
94 | + console.log(e) | |
95 | + } | |
96 | + | |
97 | + t.end() | |
78 | 98 | node.stop(() => { |
79 | - process.exit() | |
99 | + // process.exit() | |
80 | 100 | }) |
81 | 101 | }) |
82 | 102 | }) |
Built with git-ssb-web