Files: dca4a828fc87c8fdcf30901362c346ff179401d4 / index.js
4782 bytesRaw
1 | const Tree = require('merkle-radix-tree') |
2 | const Graph = require('ipld-graph-builder') |
3 | const Kernel = require('./kernel.js') |
4 | const Scheduler = require('./scheduler.js') |
5 | const DFSchecker = require('./dfsChecker.js') |
6 | const CreationService = require('./creationService.js') |
7 | |
8 | const CREATION_ID = 0 |
9 | // const ROUTING_ID = 1 |
10 | |
11 | module.exports = class Hypervisor { |
12 | /** |
13 | * The Hypervisor manages the container instances by instantiating them and |
14 | * destorying them when possible. It also facilitates localating Containers |
15 | * @param {Graph} dag an instance of [ipfs.dag](https://github.com/ipfs/interface-ipfs-core/tree/master/API/dag#dag-api) |
16 | * @param {object} state - the starting state |
17 | */ |
18 | constructor (dag, state = {'/': Tree.emptyTreeState}) { |
19 | this.graph = new Graph(dag) |
20 | this.tree = new Tree({ |
21 | graph: this.graph, |
22 | root: state |
23 | }) |
24 | this.scheduler = new Scheduler() |
25 | this.state = state |
26 | this._containerTypes = {} |
27 | this._nodesToCheck = new Set() |
28 | |
29 | this.creationService = new CreationService({ |
30 | hypervisor: this |
31 | }) |
32 | this.scheduler.systemServices.set(CREATION_ID, this.creationService) |
33 | this.pinnedIds = new Set() |
34 | |
35 | this.ROOT_ID = 'zdpuAm6aTdLVMUuiZypxkwtA7sKm7BWERy8MPbaCrFsmiyzxr' |
36 | } |
37 | |
38 | /** |
39 | * add a potaintail node in the state graph to check for garbage collection |
40 | * @param {string} id |
41 | */ |
42 | addNodeToCheck (id) { |
43 | this._nodesToCheck.add(id) |
44 | } |
45 | |
46 | /** |
47 | * given a port, this finds the corridsponeding endpoint port of the channel |
48 | * @param {object} port |
49 | * @returns {Promise} |
50 | */ |
51 | async getDestPort (port) { |
52 | if (port.destPort) { |
53 | return port.destPort |
54 | } else { |
55 | const instance = await this.scheduler.getInstance(port.destId) |
56 | let containerState |
57 | if (instance) { |
58 | containerState = instance.state |
59 | } else { |
60 | containerState = await this.tree.get(port.destId) |
61 | } |
62 | return this.graph.get(containerState, `ports/${port.destName}`) |
63 | } |
64 | } |
65 | |
66 | async send (port, message) { |
67 | const id = port.destId |
68 | if (id !== undefined) { |
69 | const instance = await this.getInstance(id) |
70 | return instance.queue(port, message) |
71 | } else { |
72 | // port is unbound |
73 | port.destPort.messages.push(message) |
74 | } |
75 | } |
76 | |
77 | // loads an instance of a container from the state |
78 | async _loadInstance (id, state) { |
79 | if (!state) { |
80 | state = await this.tree.get(id) |
81 | } |
82 | const container = this._containerTypes[state.type] |
83 | let code |
84 | |
85 | // checks if the code stored in the state is an array and that the elements |
86 | // are merkle link |
87 | if (state.code && state.code[0]['/']) { |
88 | await this.graph.tree(state.code, 1) |
89 | code = state.code.map(a => a['/']).reduce((a, b) => a + b) |
90 | } else { |
91 | code = state.code |
92 | } |
93 | |
94 | // create a new kernel instance |
95 | const kernel = new Kernel({ |
96 | hypervisor: this, |
97 | state: state, |
98 | code: code, |
99 | container: container, |
100 | id: id |
101 | }) |
102 | |
103 | // save the newly created instance |
104 | this.scheduler.update(kernel) |
105 | return kernel |
106 | } |
107 | |
108 | /** |
109 | * gets an existsing container instances |
110 | * @param {string} id - the containers ID |
111 | * @returns {Promise} |
112 | */ |
113 | async getInstance (id) { |
114 | let instance = this.scheduler.getInstance(id) |
115 | if (instance) { |
116 | return instance |
117 | } else { |
118 | const resolve = this.scheduler.lock(id) |
119 | const instance = await this._loadInstance(id) |
120 | await instance.startup() |
121 | resolve(instance) |
122 | return instance |
123 | } |
124 | } |
125 | |
126 | createInstance (message, id) { |
127 | return this.creationService.createInstance(message, id) |
128 | } |
129 | |
130 | createChannel () { |
131 | const port1 = { |
132 | messages: [] |
133 | } |
134 | |
135 | const port2 = { |
136 | messages: [], |
137 | destPort: port1 |
138 | } |
139 | |
140 | port1.destPort = port2 |
141 | return [port1, port2] |
142 | } |
143 | |
144 | /** |
145 | * creates a state root starting from a given container and a given number of |
146 | * ticks |
147 | * @param {Number} ticks the number of ticks at which to create the state root |
148 | * @returns {Promise} |
149 | */ |
150 | async createStateRoot (ticks) { |
151 | await this.scheduler.wait(ticks) |
152 | |
153 | const unlinked = await DFSchecker(this.tree, this._nodesToCheck, (id) => { |
154 | return this.pinnedIds.has(id) |
155 | }) |
156 | for (const id of unlinked) { |
157 | await this.tree.delete(id) |
158 | } |
159 | return this.graph.flush(this.state) |
160 | } |
161 | |
162 | /** |
163 | * regirsters a container with the hypervisor |
164 | * @param {Class} Constructor - a Class for instantiating the container |
165 | * @param {*} args - any args that the contructor takes |
166 | * @param {interger} typeId - the container's type identification ID |
167 | */ |
168 | registerContainer (Constructor, args, typeId = Constructor.typeId) { |
169 | this._containerTypes[typeId] = { |
170 | Constructor: Constructor, |
171 | args: args |
172 | } |
173 | } |
174 | |
175 | pin (instance) { |
176 | this.pinnedIds.add(instance.id) |
177 | } |
178 | } |
179 |
Built with git-ssb-web