Commit 4f8ba9e911f6ad620fc6cd59cc15fbfaa1fe384c
cleanup reverts and added tests for reverts
wanderer committed on 5/9/2017, 7:53:19 PMParent: 99abd38a5b872b13ce3d020a5947a5325c6c13f4
Files changed
kernel.js | changed |
package.json | changed |
portManager.js | changed |
tests/index.js | changed |
kernel.js | ||
---|---|---|
@@ -1,7 +1,9 @@ | ||
1 | 1 | const PriorityQueue = require('fastpriorityqueue') |
2 | +const clearObject = require('object-clear') | |
3 | +const clone = require('clone') | |
4 | +const BN = require('bn.js') | |
2 | 5 | const EventEmitter = require('events') |
3 | -const BN = require('bn.js') | |
4 | 6 | const PortManager = require('./portManager.js') |
5 | 7 | |
6 | 8 | module.exports = class Kernel extends EventEmitter { |
7 | 9 | constructor (opts) { |
@@ -53,58 +55,37 @@ | ||
53 | 55 | this.emit(vmState, message) |
54 | 56 | } |
55 | 57 | |
56 | 58 | async _runNextMessage () { |
57 | - await this.hypervisor.graph.get(this.entryPort, 'id') | |
58 | 59 | const message = await this.ports.getNextMessage() |
59 | - // if the vm is paused and it gets a message; save that message for use when the VM is resumed | |
60 | - if (message && this.vmState === 'paused') { | |
61 | - this.ports._portMap(message._fromPort).unshfit(message) | |
62 | - } else if (!message && this.vmState !== 'paused') { | |
60 | + if (message) { | |
61 | + // run the next message | |
62 | + this.run(message) | |
63 | + } else { | |
63 | 64 | // if no more messages then shut down |
64 | 65 | this._updateVmState('idle') |
65 | - } else { | |
66 | - // run the next message | |
67 | - this._run(message) | |
68 | 66 | } |
69 | 67 | } |
70 | 68 | |
71 | - _updateEntryPort (entryPort) { | |
72 | - // reset waits, update parent port | |
73 | - } | |
74 | - | |
75 | - destroy () { | |
76 | - // destory waits | |
77 | - } | |
78 | - | |
79 | - pause () { | |
80 | - this._setState('paused') | |
81 | - } | |
82 | - | |
83 | - resume () { | |
84 | - this._setState('running') | |
85 | - this._runNextMessage() | |
86 | - } | |
87 | - | |
88 | 69 | /** |
89 | 70 | * run the kernels code with a given enviroment |
90 | 71 | * The Kernel Stores all of its state in the Environment. The Interface is used |
91 | 72 | * to by the VM to retrive infromation from the Environment. |
92 | 73 | */ |
93 | - async _run (message) { | |
94 | - // shallow copy | |
95 | - const oldState = Object.assign({}, this.state) | |
74 | + async run (message) { | |
75 | + const oldState = clone(this.state, false, 3) | |
96 | 76 | let result |
97 | 77 | try { |
98 | 78 | result = await this.vm.run(message) || {} |
99 | 79 | } catch (e) { |
80 | + // revert the state | |
81 | + clearObject(this.state) | |
82 | + Object.assign(this.state, oldState) | |
83 | + | |
100 | 84 | result = { |
101 | 85 | exception: true, |
102 | 86 | exceptionError: e |
103 | 87 | } |
104 | - clearObject(this.state) | |
105 | - Object.assign(this.state, oldState) | |
106 | - console.log(e) | |
107 | 88 | } |
108 | 89 | |
109 | 90 | this.emit('result', result) |
110 | 91 | return result |
@@ -139,12 +120,13 @@ | ||
139 | 120 | } |
140 | 121 | } |
141 | 122 | } |
142 | 123 | |
143 | - async createPort (type, name) { | |
124 | + createPort (type, name) { | |
144 | 125 | const VM = this.hypervisor._VMs[type] |
145 | 126 | const parentId = this.entryPort ? this.entryPort.id : null |
146 | - let nonce = await this.hypervisor.graph.get(this.state, 'nonce') | |
127 | + let nonce = this.state['/'].nonce | |
128 | + | |
147 | 129 | const portRef = { |
148 | 130 | 'messages': [], |
149 | 131 | 'id': { |
150 | 132 | '/': { |
@@ -172,13 +154,7 @@ | ||
172 | 154 | message._ticks = this.ticks |
173 | 155 | |
174 | 156 | const receiverEntryPort = portRef === this.entryPort ? this.parentPort : portRef |
175 | 157 | const vm = await this.hypervisor.getInstance(receiverEntryPort) |
176 | - return vm.queue(message) | |
158 | + vm.queue(message) | |
177 | 159 | } |
178 | 160 | } |
179 | - | |
180 | -function clearObject (myObject) { | |
181 | - for (var member in myObject) { | |
182 | - delete myObject[member] | |
183 | - } | |
184 | -} |
package.json | ||
---|---|---|
@@ -26,11 +26,13 @@ | ||
26 | 26 | "contributors": "Alex Beregszaszi <alex@rtfs.hu>", |
27 | 27 | "license": "MPL-2.0", |
28 | 28 | "dependencies": { |
29 | 29 | "bn.js": "^4.11.6", |
30 | + "clone": "^2.1.1", | |
30 | 31 | "fastpriorityqueue": "^0.2.4", |
31 | 32 | "ipld-graph-builder": "1.1.5", |
32 | 33 | "multibase": "^0.3.4", |
34 | + "object-clear": "^0.1.0", | |
33 | 35 | "primea-message": "0.0.0" |
34 | 36 | }, |
35 | 37 | "devDependencies": { |
36 | 38 | "coveralls": "^2.13.1", |
portManager.js | ||
---|---|---|
@@ -56,20 +56,12 @@ | ||
56 | 56 | this.ports[name] = port |
57 | 57 | return this._mapPort(name, port) |
58 | 58 | } |
59 | 59 | |
60 | - get (port) { | |
61 | - return this._portMap.get(port) | |
60 | + get (key) { | |
61 | + return this.ports[key] | |
62 | 62 | } |
63 | 63 | |
64 | - getRef (key) { | |
65 | - if (key === ENTRY) { | |
66 | - return this.entryPort | |
67 | - } else { | |
68 | - return this.ports[key] | |
69 | - } | |
70 | - } | |
71 | - | |
72 | 64 | // waits till all ports have reached a threshold tick count |
73 | 65 | wait (threshold, fromPort) { |
74 | 66 | // find the ports that have a smaller tick count then the threshold tick count |
75 | 67 | const unkownPorts = [...this._portMap].filter(([portRef, port]) => { |
@@ -85,9 +77,8 @@ | ||
85 | 77 | |
86 | 78 | async getNextMessage () { |
87 | 79 | await this.wait(this.kernel.ticks, this.entryPort) |
88 | 80 | const portMap = [...this._portMap].reduce(messageArbiter) |
89 | - if (portMap) { | |
90 | - return portMap[1].shift() | |
91 | - } | |
81 | + // console.log('here!!!!', portMap) | |
82 | + return portMap[1].shift() | |
92 | 83 | } |
93 | 84 | } |
tests/index.js | ||
---|---|---|
@@ -90,48 +90,41 @@ | ||
90 | 90 | |
91 | 91 | // test reviving the state |
92 | 92 | class testVMContainer3 extends BaseContainer { |
93 | 93 | async run (m) { |
94 | - const port = this.kernel.ports.getRef('child') | |
94 | + const port = this.kernel.ports.get('child') | |
95 | 95 | await this.kernel.send(port, m) |
96 | 96 | this.kernel.incrementTicks(1) |
97 | 97 | } |
98 | 98 | } |
99 | 99 | |
100 | 100 | hypervisor.registerContainer('test', testVMContainer3) |
101 | 101 | root = await hypervisor.createInstance('test', stateRoot) |
102 | - port = await root.ports.getRef('first') | |
102 | + port = await root.ports.get('first') | |
103 | 103 | |
104 | 104 | await root.send(port, message) |
105 | 105 | await hypervisor.createStateRoot(root, Infinity) |
106 | 106 | |
107 | 107 | t.end() |
108 | - | |
109 | - node.stop(() => { | |
110 | - process.exit() | |
111 | - }) | |
112 | 108 | }) |
113 | 109 | |
114 | - tape.skip('ping pong', async t => { | |
110 | + tape('ping pong', async t => { | |
115 | 111 | class Ping extends BaseContainer { |
116 | 112 | async run (m) { |
117 | - console.log('ping') | |
118 | - let port = this.kernel.ports.getRef('child') | |
113 | + let port = this.kernel.ports.get('child') | |
119 | 114 | if (!port) { |
120 | 115 | port = await this.kernel.createPort('pong', 'child') |
121 | 116 | } |
122 | 117 | |
123 | 118 | if (this.kernel.ticks < 100) { |
124 | 119 | this.kernel.incrementTicks(1) |
125 | - console.log('here') | |
126 | 120 | return this.kernel.send(port, new Message()) |
127 | 121 | } |
128 | 122 | } |
129 | 123 | } |
130 | 124 | |
131 | 125 | class Pong extends BaseContainer { |
132 | 126 | run (m) { |
133 | - console.log('pong') | |
134 | 127 | const port = m.fromPort |
135 | 128 | return this.kernel.send(port, new Message()) |
136 | 129 | } |
137 | 130 | } |
@@ -139,16 +132,100 @@ | ||
139 | 132 | const hypervisor = new Hypervisor({ |
140 | 133 | dag: node.dag |
141 | 134 | }) |
142 | 135 | |
143 | - try { | |
144 | - hypervisor.registerContainer('ping', Ping) | |
145 | - hypervisor.registerContainer('pong', Pong) | |
146 | - const root = await hypervisor.createInstance('pong') | |
147 | - const port = await root.createPort('ping', 'child') | |
136 | + hypervisor.registerContainer('ping', Ping) | |
137 | + hypervisor.registerContainer('pong', Pong) | |
138 | + const root = await hypervisor.createInstance('pong') | |
139 | + const port = await root.createPort('ping', 'child') | |
148 | 140 | |
149 | - await root.send(port, new Message()) | |
150 | - } catch (e) { | |
151 | - console.log(e) | |
141 | + await root.send(port, new Message()) | |
142 | + await hypervisor.createStateRoot(root, Infinity) | |
143 | + | |
144 | + t.end() | |
145 | + }) | |
146 | + | |
147 | + tape('queing multiple messages', async t => { | |
148 | + let runs = 0 | |
149 | + | |
150 | + class Root extends BaseContainer { | |
151 | + async run (m) { | |
152 | + const one = this.kernel.createPort('child', 'one') | |
153 | + const two = this.kernel.createPort('child', 'two') | |
154 | + const three = this.kernel.createPort('child', 'three') | |
155 | + | |
156 | + await Promise.all([ | |
157 | + this.kernel.send(one, new Message()), | |
158 | + this.kernel.send(two, new Message()), | |
159 | + this.kernel.send(three, new Message()) | |
160 | + ]) | |
161 | + | |
162 | + this.kernel.incrementTicks(1) | |
163 | + } | |
152 | 164 | } |
165 | + | |
166 | + class Child extends BaseContainer { | |
167 | + run (m) { | |
168 | + return new Promise((resolve, reject) => { | |
169 | + setTimeout(() => { | |
170 | + runs++ | |
171 | + this.kernel.incrementTicks(2) | |
172 | + resolve() | |
173 | + }, 200) | |
174 | + }) | |
175 | + } | |
176 | + } | |
177 | + | |
178 | + const hypervisor = new Hypervisor({ | |
179 | + dag: node.dag | |
180 | + }) | |
181 | + | |
182 | + hypervisor.registerContainer('root', Root) | |
183 | + hypervisor.registerContainer('child', Child) | |
184 | + | |
185 | + const root = await hypervisor.createInstance('root') | |
186 | + const port = await root.createPort('root', 'first') | |
187 | + await root.send(port, new Message()) | |
188 | + await root.wait(Infinity) | |
189 | + | |
190 | + t.equals(runs, 3, 'the number of run should be 3') | |
191 | + const nonce = await hypervisor.graph.get(root.state, 'ports/first/link/nonce/0') | |
192 | + t.equals(nonce, 3, 'should have the correct nonce') | |
193 | + | |
194 | + t.end() | |
153 | 195 | }) |
196 | + | |
197 | + tape('traps', async t => { | |
198 | + class Root extends BaseContainer { | |
199 | + async run (m) { | |
200 | + await Promise.all([ | |
201 | + this.kernel.createPort('root', 'one'), | |
202 | + this.kernel.createPort('root', 'two'), | |
203 | + this.kernel.createPort('root', 'three') | |
204 | + ]) | |
205 | + | |
206 | + throw new Error('it is a trap!!!') | |
207 | + } | |
208 | + } | |
209 | + | |
210 | + const hypervisor = new Hypervisor({ | |
211 | + dag: node.dag | |
212 | + }) | |
213 | + | |
214 | + hypervisor.registerContainer('root', Root) | |
215 | + const root = await hypervisor.createInstance('root') | |
216 | + await root.run() | |
217 | + | |
218 | + t.deepEquals(root.state, { | |
219 | + '/': { | |
220 | + nonce: [0], | |
221 | + ports: {} | |
222 | + } | |
223 | + }, 'should revert the state') | |
224 | + | |
225 | + t.end() | |
226 | + | |
227 | + node.stop(() => { | |
228 | + process.exit() | |
229 | + }) | |
230 | + }) | |
154 | 231 | }) |
Built with git-ssb-web